{"title":"信号","version":"6.1","locale":"zh-hans","docname":"topics/signals","url":"/zh-hans/6.1/topics/signals/","canonical":"https://djangodocs.dev/zh-hans/6.1/topics/signals/","summary":"Django有一个“信号调度器(signal dispatcher)”，用来帮助解耦的应用获知框架内任何其他地方发生了操作。简单地说，信号允许某些 发送器 去通知一组 接收器 某些操作发生了。当许多代码段都可能对同一事件感兴趣时，信号特别有用。 例如，第三方应用程序可以注册以接收设置更改的通知： Code Copy from…","html":"<span id=\"signals\"></span><h1>信号<a class=\"heading-anchor\" href=\"#module-django.dispatch\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>Django有一个“信号调度器(signal dispatcher)”，用来帮助解耦的应用获知框架内任何其他地方发生了操作。简单地说，信号允许某些 <em>发送器</em> 去通知一组 <em>接收器</em> 某些操作发生了。当许多代码段都可能对同一事件感兴趣时，信号特别有用。</p>\n<p>例如，第三方应用程序可以注册以接收设置更改的通知：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.apps</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">AppConfig</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.core.signals</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">setting_changed</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">my_receiver</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">):</span>\n    <span class=\"nb\">print</span><span class=\"p\">(</span><span class=\"s2\">&quot;Setting changed!&quot;</span><span class=\"p\">)</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MyAppConfig</span><span class=\"p\">(</span><span class=\"n\">AppConfig</span><span class=\"p\">):</span>\n    <span class=\"o\">...</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">ready</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"n\">setting_changed</span><span class=\"o\">.</span><span class=\"n\">connect</span><span class=\"p\">(</span><span class=\"n\">my_receiver</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>Django 的 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/signals/\"><span class=\"doc\">内置信号</span></a> 允许用户代码在某些操作发生时收到通知。</p>\n<p>你也可以定义并发送自己的自定义信号。请参阅下面的 <span class=\"xref std std-ref\">定义和发送信号</span>。</p>\n<aside class=\"admonition admonition-warning\" role=\"note\">\n<p class=\"admonition-title\">Warning</p>\n<p>信号看起来是松散耦合的表现，但它们很快会导致难以理解、调整和调试的代码。</p>\n<p>在可能的情况下，你应该选择直接调用处理代码，而不是通过信号进行分发。</p>\n</aside>\n<section id=\"listening-to-signals\">\n<h2>监听信号<a class=\"heading-anchor\" href=\"#listening-to-signals\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>要接收信号，使用 <a class=\"reference internal\" href=\"#django.dispatch.Signal.connect\" title=\"django.dispatch.Signal.connect\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Signal.connect()</span></code></a> 方法注册一个 <em>接收器</em> 函数。当发送信号时调用接收器。信号的所有接收器函数都按照注册时的顺序一个接一个调用。</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.dispatch.Signal.connect\">\n<span class=\"sig-prename descclassname\"><span class=\"pre\">Signal.</span></span><span class=\"sig-name descname\"><span class=\"pre\">connect</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">receiver</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">sender</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">weak</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">True</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">dispatch_uid</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.dispatch.Signal.connect\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><dl class=\"field-list simple\">\n<dt class=\"field-odd\">参数<span class=\"colon\">:</span></dt>\n<dd class=\"field-odd\"><ul class=\"simple\">\n<li><p><strong>receiver</strong> -- 将连接到此信号的回调函数。查看 <span class=\"xref std std-ref\">接收器函数</span> 获取更多信息。</p></li>\n<li><p><strong>sender</strong> -- 指定要从其接收信号的特定发送方。查看 <span class=\"xref std std-ref\">连接到特定信号</span> 获取更多信息。</p></li>\n<li><p><strong>weak</strong> -- Django stores signal receivers as weak references by\ndefault. Thus, if your receiver is a local function, it may be\ngarbage collected. To prevent this, pass <code class=\"docutils literal notranslate\"><span class=\"pre\">weak=False</span></code> when you call\nthe signal's <code class=\"docutils literal notranslate\"><span class=\"pre\">connect()</span></code> method.</p></li>\n<li><p><strong>dispatch_uid</strong> -- 在可能发送重复信号的情况下，信号接收器的唯一标识符。查看 <a class=\"reference internal\" href=\"#preventing-duplicate-signals\"><span class=\"std std-ref\">防止重复信号</span></a> 获取更多信息。</p></li>\n</ul>\n</dd>\n</dl>\n</dd></dl>\n\n<p>让我们通过注册一个在每个HTTP请求完成后被调用的信号来看看这是如何工作的。我们将连接到 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/signals/#django.core.signals.request_finished\" title=\"django.core.signals.request_finished\"><code class=\"xref py py-data docutils literal notranslate\"><span class=\"pre\">request_finished</span></code></a> 信号。</p>\n<section id=\"receiver-functions\">\n<span id=\"id1\"></span><h3>接收器函数<a class=\"heading-anchor\" href=\"#receiver-functions\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>首先，我们需要定义一个接收器函数。一个接收器可以是任何 Python 函数或方法：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">my_receiver</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">):</span>\n    <span class=\"nb\">print</span><span class=\"p\">(</span><span class=\"s2\">&quot;Request finished!&quot;</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>Notice that the function takes a <code class=\"docutils literal notranslate\"><span class=\"pre\">sender</span></code> argument, along with wildcard\nkeyword arguments (<code class=\"docutils literal notranslate\"><span class=\"pre\">**kwargs</span></code>); all signal receivers must take these\narguments.</p>\n<p>We'll look at senders <a class=\"reference internal\" href=\"#connecting-to-specific-signals\"><span class=\"std std-ref\">a bit later</span></a>, but\nright now look at the <code class=\"docutils literal notranslate\"><span class=\"pre\">**kwargs</span></code> argument. All signals send keyword\narguments, and may change those keyword arguments at any time. In the case of\n<a class=\"reference internal\" href=\"/zh-hans/6.1/ref/signals/#django.core.signals.request_finished\" title=\"django.core.signals.request_finished\"><code class=\"xref py py-data docutils literal notranslate\"><span class=\"pre\">request_finished</span></code></a>, it's documented as sending no\narguments, which means we might be tempted to write our signal handling as\n<code class=\"docutils literal notranslate\"><span class=\"pre\">my_receiver(sender)</span></code>.</p>\n<p>这是错误的——事实上，如果这样做，Django 将抛出一个错误。这是因为在任何时候，参数都可能被添加到信号中，而你的接收器必须能够处理这些新的参数。</p>\n<p>接收器也可以是异步函数，具有相同的签名，但使用 <code class=\"docutils literal notranslate\"><span class=\"pre\">async</span> <span class=\"pre\">def</span></code> 声明：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">async</span> <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">my_receiver</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">):</span>\n    <span class=\"k\">await</span> <span class=\"n\">asyncio</span><span class=\"o\">.</span><span class=\"n\">sleep</span><span class=\"p\">(</span><span class=\"mi\">5</span><span class=\"p\">)</span>\n    <span class=\"nb\">print</span><span class=\"p\">(</span><span class=\"s2\">&quot;Request finished!&quot;</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>信号可以同步或异步发送，接收器将自动适应正确的调用方式。更多信息请参阅 <a class=\"reference internal\" href=\"#sending-signals\"><span class=\"std std-ref\">发送信号</span></a>。</p>\n</section>\n<section id=\"connecting-receiver-functions\">\n<span id=\"id2\"></span><h3>连接接收器函数<a class=\"heading-anchor\" href=\"#connecting-receiver-functions\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>有两种方法可以将接收器连接到信号。你可以选择手动连接线路：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.core.signals</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">request_finished</span>\n\n<span class=\"n\">request_finished</span><span class=\"o\">.</span><span class=\"n\">connect</span><span class=\"p\">(</span><span class=\"n\">my_receiver</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>或者，你可以使用一个 <a class=\"reference internal\" href=\"#django.dispatch.receiver\" title=\"django.dispatch.receiver\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">receiver()</span></code></a> 装饰器：</p>\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.dispatch.receiver\">\n<span class=\"sig-name descname\"><span class=\"pre\">receiver</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">signal</span></span></em>, <em class=\"sig-param\"><span class=\"o\"><span class=\"pre\">**</span></span><span class=\"n\"><span class=\"pre\">kwargs</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.dispatch.receiver\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><dl class=\"field-list simple\">\n<dt class=\"field-odd\">参数<span class=\"colon\">:</span></dt>\n<dd class=\"field-odd\"><ul class=\"simple\">\n<li><p><strong>signal</strong> -- 一个用于连接函数的信号或包含多个信号的列表。</p></li>\n<li><p><strong>kwargs</strong> -- 通配符关键字参数可传递给 <a class=\"reference internal\" href=\"#receiver-functions\"><span class=\"std std-ref\">函数</span></a>。</p></li>\n</ul>\n</dd>\n</dl>\n</dd></dl>\n\n<p>以下是你如何使用装饰器连接：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.core.signals</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">request_finished</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.dispatch</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">receiver</span>\n\n\n<span class=\"nd\">@receiver</span><span class=\"p\">(</span><span class=\"n\">request_finished</span><span class=\"p\">)</span>\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">my_receiver</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">):</span>\n    <span class=\"nb\">print</span><span class=\"p\">(</span><span class=\"s2\">&quot;Request finished!&quot;</span><span class=\"p\">)</span>\n</code></pre></div>\n<p>Now, our <code class=\"docutils literal notranslate\"><span class=\"pre\">my_receiver</span></code> function will be called each time a request finishes.</p>\n<aside class=\"admonition-where-should-this-code-live admonition\">\n<p class=\"admonition-title\">我的代码该放在哪？</p>\n<p>严格来说，信号处理和注册的代码可以放在任何你喜欢的地方，但是推荐避免放在应用程序的根目录和 <code class=\"docutils literal notranslate\"><span class=\"pre\">models</span></code> 模块内以尽量减少导入代码的副作用。</p>\n<p>In practice, signal receivers are usually defined in a <code class=\"docutils literal notranslate\"><span class=\"pre\">signals</span></code>\nsubmodule of the application they relate to. Signal receivers are\nconnected in the <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/applications/#django.apps.AppConfig.ready\" title=\"django.apps.AppConfig.ready\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">ready()</span></code></a> method of your\napplication <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/applications/#configuring-applications-ref\"><span class=\"std std-ref\">configuration class</span></a>. If\nyou're using the <a class=\"reference internal\" href=\"#django.dispatch.receiver\" title=\"django.dispatch.receiver\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">receiver()</span></code></a> decorator, import the <code class=\"docutils literal notranslate\"><span class=\"pre\">signals</span></code>\nsubmodule inside <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/applications/#django.apps.AppConfig.ready\" title=\"django.apps.AppConfig.ready\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">ready()</span></code></a>, this will implicitly\nconnect signal receivers:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.apps</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">AppConfig</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.core.signals</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">request_finished</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MyAppConfig</span><span class=\"p\">(</span><span class=\"n\">AppConfig</span><span class=\"p\">):</span>\n    <span class=\"o\">...</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">ready</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"c1\"># Implicitly connect signal receivers decorated with @receiver.</span>\n        <span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">.</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">signals</span>\n\n        <span class=\"c1\"># Explicitly connect a signal handler.</span>\n        <span class=\"n\">request_finished</span><span class=\"o\">.</span><span class=\"n\">connect</span><span class=\"p\">(</span><span class=\"n\">signals</span><span class=\"o\">.</span><span class=\"n\">my_receiver</span><span class=\"p\">)</span>\n</code></pre></div>\n</aside>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Note</p>\n<p>The <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/applications/#django.apps.AppConfig.ready\" title=\"django.apps.AppConfig.ready\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">ready()</span></code></a> method may be executed more than\nonce during testing, so you may want to <a class=\"reference internal\" href=\"#preventing-duplicate-signals\"><span class=\"std std-ref\">guard your signals from\nduplication</span></a> if your receiver is a bound\nmethod on an instance that may be recreated.</p>\n</aside>\n</section>\n<section id=\"connecting-to-signals-sent-by-specific-senders\">\n<span id=\"connecting-to-specific-signals\"></span><h3>连接到特定发送器发送的信号<a class=\"heading-anchor\" href=\"#connecting-to-signals-sent-by-specific-senders\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>有些信号被多次发送，但你只对接收这些信号的某个子集感兴趣。例如，仔细考虑 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/signals/#django.db.models.signals.pre_save\" title=\"django.db.models.signals.pre_save\"><code class=\"xref py py-data docutils literal notranslate\"><span class=\"pre\">django.db.models.signals.pre_save</span></code></a> 在模型保存之前发送的信号。大多数时候，你不需要知道 <em>任何</em> 模型何时被保存——只需要知道某个 <em>特定</em> 模型何时被保存。</p>\n<p>在这些情况下，您可以注册以接收仅由特定发送者发送的信号。在接收 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/signals/#django.db.models.signals.pre_save\" title=\"django.db.models.signals.pre_save\"><code class=\"xref py py-data docutils literal notranslate\"><span class=\"pre\">django.db.models.signals.pre_save</span></code></a> 信号时 ，发送器会是要保存的模型类，因此你就可以表明你想要某个模型发送的信号：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.db.models.signals</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">pre_save</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.dispatch</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">receiver</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">myapp.models</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">MyModel</span>\n\n\n<span class=\"nd\">@receiver</span><span class=\"p\">(</span><span class=\"n\">pre_save</span><span class=\"p\">,</span> <span class=\"n\">sender</span><span class=\"o\">=</span><span class=\"n\">MyModel</span><span class=\"p\">)</span>\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">my_handler</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">):</span> <span class=\"o\">...</span>\n</code></pre></div>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">my_handler</span></code> 函数将仅在 <code class=\"docutils literal notranslate\"><span class=\"pre\">MyModel</span></code> 实例保存后被调用。</p>\n<p>不同的信号使用不同的对象作为它们的发送者；你需要查阅 <a class=\"reference internal\" href=\"/zh-hans/6.1/ref/signals/\"><span class=\"doc\">内置信号文档</span></a> 了解每个特定信号的详细信息。</p>\n</section>\n<section id=\"preventing-duplicate-signals\">\n<span id=\"id3\"></span><h3>防止重复信号<a class=\"heading-anchor\" href=\"#preventing-duplicate-signals\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>When <code class=\"docutils literal notranslate\"><span class=\"pre\">dispatch_uid</span></code> is not provided, Django identifies each receiver using\nits Python object identity and registers it only once. For module-level\nfunctions, static methods, and class methods, the identity is stable, so\nconnecting the same receiver more than once has no effect:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">my_handler</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"p\">,</span> <span class=\"o\">**</span><span class=\"n\">kwargs</span><span class=\"p\">):</span> <span class=\"o\">...</span>\n\n\n<span class=\"n\">my_signal</span><span class=\"o\">.</span><span class=\"n\">connect</span><span class=\"p\">(</span><span class=\"n\">my_handler</span><span class=\"p\">)</span>  <span class=\"c1\"># Running this code again is a no-op.</span>\n</code></pre></div>\n<p>Bound methods, which take a <code class=\"docutils literal notranslate\"><span class=\"pre\">self</span></code> argument, are different. Their identity\nis tied to the specific instance, so connecting the same method from a new\ninstance registers it as an additional receiver:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">connect_signals</span><span class=\"p\">():</span>\n    <span class=\"n\">backend</span> <span class=\"o\">=</span> <span class=\"n\">Backend</span><span class=\"p\">()</span>\n    <span class=\"n\">my_signal</span><span class=\"o\">.</span><span class=\"n\">connect</span><span class=\"p\">(</span><span class=\"n\">backend</span><span class=\"o\">.</span><span class=\"n\">my_handler</span><span class=\"p\">)</span>  <span class=\"c1\"># A distinct receiver.</span>\n\n\n<span class=\"n\">connect_signals</span><span class=\"p\">()</span>  <span class=\"c1\"># Running this code again registers another receiver.</span>\n</code></pre></div>\n<p>When using a bound method as a receiver, multiple registrations can be\nprevented by supplying a unique <code class=\"docutils literal notranslate\"><span class=\"pre\">dispatch_uid</span></code>. This identifier will usually\nbe a string, although any hashable object will suffice. The receiver will only\nbe bound to the signal once for each unique <code class=\"docutils literal notranslate\"><span class=\"pre\">dispatch_uid</span></code> value:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.core.signals</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">request_finished</span>\n\n<span class=\"n\">request_finished</span><span class=\"o\">.</span><span class=\"n\">connect</span><span class=\"p\">(</span><span class=\"n\">my_receiver</span><span class=\"p\">,</span> <span class=\"n\">dispatch_uid</span><span class=\"o\">=</span><span class=\"s2\">&quot;my_unique_identifier&quot;</span><span class=\"p\">)</span>\n</code></pre></div>\n</section>\n</section>\n<section id=\"defining-and-sending-signals\">\n<span id=\"id4\"></span><h2>定义和发送信号<a class=\"heading-anchor\" href=\"#defining-and-sending-signals\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>您的应用程序可以利用信号基础设施并提供自己的信号。</p>\n<aside class=\"admonition-when-to-use-custom-signals admonition\">\n<p class=\"admonition-title\">何时使用自定义信号</p>\n<p>信号是隐式函数调用，这使得调试更加困难。如果你的自定义信号的发送器和接收器都在你的项目内，最好使用显式函数调用。</p>\n</aside>\n<section id=\"defining-signals\">\n<h3>定义信号<a class=\"heading-anchor\" href=\"#defining-signals\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<dl class=\"py class\">\n<dt class=\"sig sig-object py\" id=\"django.dispatch.Signal\">\n<em class=\"property\"><span class=\"k\"><span class=\"pre\">class</span></span><span class=\"w\"> </span></em><span class=\"sig-name descname\"><span class=\"pre\">Signal</span></span><a class=\"heading-anchor\" href=\"#django.dispatch.Signal\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p>所有的信号都是 <a class=\"reference internal\" href=\"#django.dispatch.Signal\" title=\"django.dispatch.Signal\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">django.dispatch.Signal</span></code></a> 的实例。</p>\n<p>例如：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">import</span><span class=\"w\"> </span><span class=\"nn\">django.dispatch</span>\n\n<span class=\"n\">pizza_done</span> <span class=\"o\">=</span> <span class=\"n\">django</span><span class=\"o\">.</span><span class=\"n\">dispatch</span><span class=\"o\">.</span><span class=\"n\">Signal</span><span class=\"p\">()</span>\n</code></pre></div>\n<p>这声明了一个 <code class=\"docutils literal notranslate\"><span class=\"pre\">pizza_done</span></code> 信号。</p>\n</section>\n<section id=\"sending-signals\">\n<span id=\"id5\"></span><h3>发送信号<a class=\"heading-anchor\" href=\"#sending-signals\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>在 Django 中有两种方式可以同步发送信号。</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.dispatch.Signal.send\">\n<span class=\"sig-prename descclassname\"><span class=\"pre\">Signal.</span></span><span class=\"sig-name descname\"><span class=\"pre\">send</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">sender</span></span></em>, <em class=\"sig-param\"><span class=\"o\"><span class=\"pre\">**</span></span><span class=\"n\"><span class=\"pre\">kwargs</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.dispatch.Signal.send\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.dispatch.Signal.send_robust\">\n<span class=\"sig-prename descclassname\"><span class=\"pre\">Signal.</span></span><span class=\"sig-name descname\"><span class=\"pre\">send_robust</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">sender</span></span></em>, <em class=\"sig-param\"><span class=\"o\"><span class=\"pre\">**</span></span><span class=\"n\"><span class=\"pre\">kwargs</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.dispatch.Signal.send_robust\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p>信号也可以异步发送。</p>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.dispatch.Signal.asend\">\n<span class=\"sig-prename descclassname\"><span class=\"pre\">Signal.</span></span><span class=\"sig-name descname\"><span class=\"pre\">asend</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">sender</span></span></em>, <em class=\"sig-param\"><span class=\"o\"><span class=\"pre\">**</span></span><span class=\"n\"><span class=\"pre\">kwargs</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.dispatch.Signal.asend\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.dispatch.Signal.asend_robust\">\n<span class=\"sig-prename descclassname\"><span class=\"pre\">Signal.</span></span><span class=\"sig-name descname\"><span class=\"pre\">asend_robust</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">sender</span></span></em>, <em class=\"sig-param\"><span class=\"o\"><span class=\"pre\">**</span></span><span class=\"n\"><span class=\"pre\">kwargs</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.dispatch.Signal.asend_robust\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p>要发送信号，请调用 <a class=\"reference internal\" href=\"#django.dispatch.Signal.send\" title=\"django.dispatch.Signal.send\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Signal.send()</span></code></a>、<a class=\"reference internal\" href=\"#django.dispatch.Signal.send_robust\" title=\"django.dispatch.Signal.send_robust\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Signal.send_robust()</span></code></a>、<a class=\"reference internal\" href=\"#django.dispatch.Signal.asend\" title=\"django.dispatch.Signal.asend\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">await</span> <span class=\"pre\">Signal.asend()</span></code></a> 或 <a class=\"reference internal\" href=\"#django.dispatch.Signal.asend_robust\" title=\"django.dispatch.Signal.asend_robust\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">await</span> <span class=\"pre\">Signal.asend_robust()</span></code></a>。你必须提供 <code class=\"docutils literal notranslate\"><span class=\"pre\">sender</span></code> 参数（通常是一个类），并可以提供任意多个其他关键字参数。</p>\n<p>例如，发送 <code class=\"docutils literal notranslate\"><span class=\"pre\">pizza_done</span></code> 信号可能看起来如下：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">PizzaStore</span><span class=\"p\">:</span>\n    <span class=\"o\">...</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">send_pizza</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">toppings</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">):</span>\n        <span class=\"n\">pizza_done</span><span class=\"o\">.</span><span class=\"n\">send</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"o\">=</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"vm\">__class__</span><span class=\"p\">,</span> <span class=\"n\">toppings</span><span class=\"o\">=</span><span class=\"n\">toppings</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"o\">=</span><span class=\"n\">size</span><span class=\"p\">)</span>\n        <span class=\"o\">...</span>\n</code></pre></div>\n<p>这四种方法都返回一个元组对的列表 <code class=\"docutils literal notranslate\"><span class=\"pre\">[(receiver,</span> <span class=\"pre\">response),</span> <span class=\"pre\">...]</span></code>，表示被调用的接收器函数列表和它们的响应值。</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">send()</span></code> 和 <code class=\"docutils literal notranslate\"><span class=\"pre\">send_robust()</span></code> 在处理接收器函数所引发异常的方式上有所不同。 <code class=\"docutils literal notranslate\"><span class=\"pre\">send()</span></code> <em>不</em> 捕获接收器引起的任何异常；它只是允许错误传播。因此，并非所有的接收器都会在出现错误时被通知信号。</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">send_robust()</span></code> 捕获从 Python的 <code class=\"docutils literal notranslate\"><span class=\"pre\">Exception</span></code> 类派生的所有错误，并确保所有接收器都收到信号通知。如果发生错误，将在引发错误的接收器的元组对中返回错误实例。</p>\n<p>回溯出现在调用 <code class=\"docutils literal notranslate\"><span class=\"pre\">send_robust()</span></code> 时返回的错误中的 <code class=\"docutils literal notranslate\"><span class=\"pre\">__traceback__</span></code> 属性中。</p>\n<p>''asend（）'' 类似于 ''send（）''，但它是一个必须等待的协程：</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"k\">async</span> <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">asend_pizza</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">toppings</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"p\">):</span>\n    <span class=\"k\">await</span> <span class=\"n\">pizza_done</span><span class=\"o\">.</span><span class=\"n\">asend</span><span class=\"p\">(</span><span class=\"n\">sender</span><span class=\"o\">=</span><span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"vm\">__class__</span><span class=\"p\">,</span> <span class=\"n\">toppings</span><span class=\"o\">=</span><span class=\"n\">toppings</span><span class=\"p\">,</span> <span class=\"n\">size</span><span class=\"o\">=</span><span class=\"n\">size</span><span class=\"p\">)</span>\n    <span class=\"o\">...</span>\n</code></pre></div>\n<p>Whether synchronous or asynchronous, receivers will be correctly adapted to\nwhether <code class=\"docutils literal notranslate\"><span class=\"pre\">send()</span></code> or <code class=\"docutils literal notranslate\"><span class=\"pre\">asend()</span></code> is used. Synchronous receivers will be\ncalled using <a class=\"reference internal\" href=\"/zh-hans/6.1/topics/async/#asgiref.sync.sync_to_async\" title=\"asgiref.sync.sync_to_async\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">sync_to_async()</span></code></a> when invoked via <code class=\"docutils literal notranslate\"><span class=\"pre\">asend()</span></code>. Asynchronous\nreceivers will be called using <a class=\"reference internal\" href=\"/zh-hans/6.1/topics/async/#asgiref.sync.async_to_sync\" title=\"asgiref.sync.async_to_sync\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">async_to_sync()</span></code></a> when invoked via\n<code class=\"docutils literal notranslate\"><span class=\"pre\">send()</span></code>. Similar to the <a class=\"reference internal\" href=\"/zh-hans/6.1/topics/async/#async-performance\"><span class=\"std std-ref\">case for middleware</span></a>,\nthere is a small performance cost to adapting receivers in this way. Note that\nin order to reduce the number of sync/async calling-style switches within a\n<code class=\"docutils literal notranslate\"><span class=\"pre\">send()</span></code> or <code class=\"docutils literal notranslate\"><span class=\"pre\">asend()</span></code> call, the receivers are grouped by whether or not\nthey are async before being called. This means that an asynchronous receiver\nregistered before a synchronous receiver may be executed after the synchronous\nreceiver. In addition, async receivers are executed concurrently using\n<a class=\"reference external\" href=\"https://docs.python.org/3/library/asyncio-task.html#asyncio.TaskGroup\" title=\"(in Python v3.14)\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">asyncio.TaskGroup</span></code></a>.</p>\n<p>除了在异步请求-响应周期中的信号外，所有内置信号都使用 <a class=\"reference internal\" href=\"#django.dispatch.Signal.send\" title=\"django.dispatch.Signal.send\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Signal.send()</span></code></a> 进行分发。</p>\n<aside class=\"version-note version-changed\" data-version=\"6.1\">\n<p class=\"version-note-title\">Changed in Django 6.1</p><p>In older versions, async receivers were executed via <code class=\"docutils literal notranslate\"><span class=\"pre\">asyncio.gather()</span></code>.</p>\n</aside>\n</section>\n</section>\n<section id=\"disconnecting-signals\">\n<h2>断开信号<a class=\"heading-anchor\" href=\"#disconnecting-signals\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<dl class=\"py method\">\n<dt class=\"sig sig-object py\" id=\"django.dispatch.Signal.disconnect\">\n<span class=\"sig-prename descclassname\"><span class=\"pre\">Signal.</span></span><span class=\"sig-name descname\"><span class=\"pre\">disconnect</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">receiver</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">sender</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">dispatch_uid</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.dispatch.Signal.disconnect\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd></dd></dl>\n\n<p>要从信号中断开接收者，调用 <a class=\"reference internal\" href=\"#django.dispatch.Signal.disconnect\" title=\"django.dispatch.Signal.disconnect\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Signal.disconnect()</span></code></a>。参数与 <a class=\"reference internal\" href=\"#django.dispatch.Signal.connect\" title=\"django.dispatch.Signal.connect\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">Signal.connect()</span></code></a> 中描述的一样。如果成功断开接收者，该方法返回 <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code>，否则返回 <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code>。当将 <code class=\"docutils literal notranslate\"><span class=\"pre\">sender</span></code> 作为对 <code class=\"docutils literal notranslate\"><span class=\"pre\">&lt;app</span> <span class=\"pre\">label&gt;.&lt;model&gt;</span></code> 的延迟引用传递时，该方法始终返回 <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code>。</p>\n<p><code class=\"docutils literal notranslate\"><span class=\"pre\">receiver</span></code> 参数表明要断开的接收器。它可以是 <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code> 如果 <code class=\"docutils literal notranslate\"><span class=\"pre\">dispatch_uid</span></code> 已经被用来标识接收器。</p>\n</section>","rootId":"module-django.dispatch","toc":[{"title":"监听信号","anchor":"listening-to-signals","children":[{"title":"接收器函数","anchor":"receiver-functions","children":[]},{"title":"连接接收器函数","anchor":"connecting-receiver-functions","children":[]},{"title":"连接到特定发送器发送的信号","anchor":"connecting-to-signals-sent-by-specific-senders","children":[]},{"title":"防止重复信号","anchor":"preventing-duplicate-signals","children":[]}]},{"title":"定义和发送信号","anchor":"defining-and-sending-signals","children":[{"title":"定义信号","anchor":"defining-signals","children":[]},{"title":"发送信号","anchor":"sending-signals","children":[]}]},{"title":"断开信号","anchor":"disconnecting-signals","children":[]}],"breadcrumbs":[{"docname":"topics/index","title":"使用 Django","url":"/zh-hans/6.1/topics/"}],"prev":{"docname":"topics/settings","title":"Django 配置","url":"/zh-hans/6.1/topics/settings/"},"next":{"docname":"topics/checks","title":"系统检查框架","url":"/zh-hans/6.1/topics/checks/"},"formats":{"html":"/zh-hans/6.1/topics/signals/","markdown":"/zh-hans/6.1/topics/signals.md","json":"/zh-hans/6.1/topics/signals.json"},"source":"https://github.com/django/django/blob/stable/6.1.x/docs/topics/signals.txt","official":"https://docs.djangoproject.com/zh-hans/6.1/topics/signals/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0"],"inLocales":["en","sv","zh-hans","ga","fr","ja","id","it","pt-br","ko","es","el","pl"]}