信号¶ T0>

Django包含一个“信号调度程序”,它可以帮助解耦的应用程序在框架中的其他地方发生操作时得到通知。 简而言之,信号允许某些发送者通知一组接收者发生了某些操作。 当许多代码可能对同一事件感兴趣时,它们特别有用。

Django提供了set of built-in signals,可以让用户代码通过Django本身通知某些操作。 这些包括一些有用的通知:

请参阅built-in signal documentation以获取完整列表以及每个信号的完整说明。

您也可以定义并发送您自己的自定义信号;见下文。

听信号

要接收信号,请使用Signal.connect()方法注册receiver函数。 接收器功能在信号发送时被调用。

信号。connect(receiver, sender=None, weak=True, dispatch_uid=None)[source]
参数:
  • receiver - 将被连接到这个信号的回调函数。 有关更多信息,请参阅Receiver functions
  • 发件人 - 指定一个特定的发件人接收信号。 请参阅Connecting to signals sent by specific senders了解更多信息。
  • - Django默认将信号处理程序存储为弱引用。 因此,如果您的接收器是本地功能,则可能会收集垃圾。 为了防止这种情况发生,当您调用信号的connect()方法时,传递weak=False
  • dispatch_uid – A unique identifier for a signal receiver in cases where duplicate signals may be sent. 请参阅Preventing duplicate signals了解更多信息。

通过注册一个在每个HTTP请求完成后被调用的信号,我们来看看它是如何工作的。 我们将连接到request_finished信号。

接收函数

首先,我们需要定义一个接收函数。 接收器可以是任何Python函数或方法:

def my_callback(sender, **kwargs):
    print("Request finished!")

请注意,函数带有sender参数,以及通配符关键字参数(**kwargs);所有的信号处理程序必须采取这些论据。

We’ll look at senders a bit later, but right now look at the **kwargs argument. 所有信号都发送关键字参数,并可能随时更改这些关键字参数。 request_finished的情况下,它被记录为不发送参数,这意味着我们可能会试图将我们的信号处理写为my_callback(sender)

这将是错误的 - 事实上,如果你这样做,Django会抛出一个错误。 这是因为在任何时候,参数都可能被添加到信号中,您的接收器必须能够处理这些新的参数。

连接接收器功能

有两种方法可以将接收器连接到信号。 您可以采取手动连接路线:

from django.core.signals import request_finished

request_finished.connect(my_callback)

或者,您可以使用receiver()修饰符:

receiver(signal)[source]
参数:信号 - 连接功能的信号或信号列表。

以下是你如何连接装饰者:

from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished)
def my_callback(sender, **kwargs):
    print("Request finished!")

现在,每当请求结束,我们的my_callback函数就会被调用。

这个代码应该在哪里居住?

严格来说,信号处理和注册码可以在任何你喜欢的地方生存,尽管建议避开应用程序的根模块和models模块来尽量减少导入代码的副作用。

实际上,信号处理程序通常是在它们相关的应用程序的signals子模块中定义的。 信号接收器连接在应用程序配置类的ready()方法中。 如果您使用receiver()修饰器,只需在ready()中导入signals子模块。

注意

The ready() method may be executed more than once during testing, so you may want to guard your signals from duplication, especially if you’re planning to send them within tests.

连接到特定发件人发送的信号

有些信号会被多次发送,但是您只会对接收这些信号的某个子集感兴趣。 例如,考虑保存模型之前发送的django.db.models.signals.pre_save信号。 Most of the time, you don’t need to know when any model gets saved – just when one specific model is saved.

在这些情况下,您可以注册接收仅由特定发件人发送的信号。 django.db.models.signals.pre_save的情况下,发件人将是保存的模型类,因此您可以指示您只需要某个模型发送的信号:

from django.db.models.signals import pre_save
from django.dispatch import receiver
from myapp.models import MyModel


@receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
    ...

my_handler函数只有在保存MyModel的实例时才会被调用。

不同的信号使用不同的对象作为发送者;你需要参考built-in signal documentation了解每个特定信号的细节。

防止重复信号

在某些情况下,将接收器连接到信号的代码可能会运行多次。 这可能会导致您的接收器功能被多次注册,因此多次调用一个信号事件。

If this behavior is problematic (such as when using signals to send an email whenever a model is saved), pass a unique identifier as the dispatch_uid argument to identify your receiver function. 这个标识符通常是一个字符串,虽然任何可哈希对象就足够了。 The end result is that your receiver function will only be bound to the signal once for each unique dispatch_uid value:

from django.core.signals import request_finished

request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")

定义和发送信号

您的应用程序可以利用信号基础设施并提供自己的信号。

定义信号

Signal(providing_args=list)[source]

All signals are django.dispatch.Signal instances. The providing_args is a list of the names of arguments the signal will provide to listeners. 然而,这纯粹是文档化的,因为没有任何东西可以检查信号是否真的为听众提供了这些论据。

例如:

import django.dispatch

pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

这将声明一个pizza_done信号,它将为接收器提供toppingssize个参数。

请记住,您可以随时更改这个参数列表,因此在第一次尝试中获得API是没有必要的。

发送信号

在Django中有两种发送信号的方法。

信号。sendsender** kwargs[source]
信号。send_robustsender** kwargs[source]

要发送信号,请调用Signal.send()(所有内置信号都使用此信号)或Signal.send_robust() 您必须提供sender参数(大部分时间是类),并且可以提供尽可能多的其他关键字参数。

例如,以下是发送我们的pizza_done信号的外观:

class PizzaStore:
    ...

    def send_pizza(self, toppings, size):
        pizza_done.send(sender=self.__class__, toppings=toppings, size=size)
        ...

Both send() and send_robust() return a list of tuple pairs [(接收器, 响应), ... ],表示被调用的接收函数的列表及其响应值。

send() differs from send_robust() in how exceptions raised by receiver functions are handled. send() does not catch any exceptions raised by receivers; it simply allows errors to propagate. 因此,在面对错误时,并不是所有的接收机都可以被通知信号。

send_robust() catches all errors derived from Python’s Exception class, and ensures all receivers are notified of the signal. 如果发生错误,错误实例将返回到引发错误的接收方的元组对中。

在调用send_robust()时返回的错误的__traceback__属性上存在回溯。

断开信号

信号。disconnect(receiver=None, sender=None, dispatch_uid=None)[source]

要从信号中断开接收机,请调用Signal.disconnect() 参数如Signal.connect() 如果接收器断开连接,则返回True,否则返回False

receiver参数指示已注册的接收方断开连接。 如果使用dispatch_uid来标识接收者,则可能是None