信号
很明显,翻译的时候确实没有中文的。
Django包含一个“信号分配器”,当在框架中其他位置发生操作时,该信号分配器可帮助通知已分离的应用程序。
简而言之,信号使某些发送者可以通知一组接收者已经采取了某些措施。
当许多代码片段可能对同一事件感兴趣时,它们特别有用。
Django提供了一组内置信号,这些信号使Django本身可以将某些操作通知给用户代码。
其中包括一些有用的通知:
请参阅内置信号文档以获取完整列表以及每个信号的完整说明。
您还可以定义和发送自己的自定义信号。
要接收信号,请使用Signal.connect()方法注册一个接收器函数。
发送信号时将调用接收器功能。
信号的所有接收器功能都按照其注册的顺序一次被调用。
Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None)
参数:
通过注册在每个HTTP请求完成后调用的信号,让我们看看它是如何工作的。
我们将连接到request_finished信号。
首先,我们需要定义一个接收器函数。
接收者可以是任何Python函数或方法:
def my_callback(sender, **kwargs):
print("Request finished!")
注意,该函数带有一个sender参数以及通配符关键字参数(**kwargs);所有信号处理程序都必须采用这些参数。
我们稍后再看发送者,但现在看一下**kwargs参数。
所有信号都发送关键字自变量,并且可以随时更改这些关键字自变量。
在request_finished的情况下,它被记录为不发送任何参数,这意味着我们可能很想将信号处理编写为my_callback(sender)。
这将是错误的,实际上,如果您这样做,Django将抛出错误。
那是因为在任何时候参数都可以添加到信号中,并且您的接收器必须能够处理这些新参数。
您可以通过两种方式将接收器连接到信号。
from django.core.signals import request_finished
request_finished.connect(my_callback)
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函数。
我的代码该放在哪?
严格来说,信号处理和注册代码可以存在于您喜欢的任何位置,尽管建议避免使用应用程序的根模块及其模型模块,以最大程度地减少导入代码的副作用。
实际上,信号处理程序通常在它们所涉及的应用程序的信号子模块中定义。 信号接收器连接在应用程序配置类的ready()方法中。 如果您使用的是receiver()装饰器,则将信号子模块导入ready()中。
注解:
在测试过程中,ready()方法可能会执行多次,因此,您可能希望防止信号重复,特别是如果您打算在测试中发送它们时。
有些信号会发送多次,但是您只会对接收这些信号的某个子集感兴趣。
例如,考虑在保存模型之前发送的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):
...
仅当保存MyModel实例时,才会调用my_handler函数。
不同的信号使用不同的对象作为其发送者。
您需要查阅内置信号文档以获取每个特定信号的详细信息。
在某些情况下,将接收器连接到信号的代码可能会运行多次。
这可能导致您的接收器功能被多次注册,因此对于单个信号事件将被多次调用。
如果此行为有问题(例如,在保存模型时使用信号发送电子邮件),请传递唯一的标识符作为dispatch_uid参数来标识您的接收器功能。
该标识符通常是一个字符串,尽管任何可哈希对象都足够。
最终结果是,对于每个唯一的dispatch_uid值,您的接收器函数将仅与信号绑定一次:
from django.core.signals import request_finished
request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")
您的应用程序可以利用信号基础结构并提供自己的信号。
何时使用自定义信号
信号是隐式函数调用,这会使调试更加困难。
如果自定义信号的发送者和接收者都在项目中,那么最好使用显式函数调用。
class Signal(providing_args=list)
所有信号都是django.dispatch.Signal实例。
provider_args是信号将提供给侦听器的参数名称的列表。
但是,这纯粹是文档性的,因为没有什么可以检查信号是否确实将这些参数提供给其侦听器的。
例如:
import django.dispatch
pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
这声明了pizza_done信号,该信号将为接收者提供 toppings 和 size 参数。
请记住,您可以随时更改此参数列表,因此无需在第一次尝试时就正确设置API。
在Django中,有两种发送信号的方法。
Signal.send(sender, **kwargs)
Signal.send_robust(sender, **kwargs)
要发送信号,请调用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)
...
send()和send_robust()都返回一个元组对[(receiver,response),…]的列表,它们表示被调用的接收器函数及其响应值的列表。
send()与send_robust()的不同之处在于如何处理接收器函数引发的异常。
send()不会捕获接收方引发的任何异常;
它只是允许错误传播。
因此,面对错误,可能不会将信号通知所有接收器。
send_robust()捕获从Python的Exception类派生的所有错误,并确保将信号通知所有接收者。
如果发生错误,则在引发错误的接收器的元组对中返回错误实例。
调用send_robust()时,返回的错误的__traceback__属性上提供了追溯。
Signal.disconnect(receiver=None, sender=None, dispatch_uid=None)
要断开接收器与信号的连接,请调用Signal.disconnect()。
参数如Signal.connect()中所述。
如果接收器已断开连接,则该方法返回True,否则返回False。
接收方参数指示已注册的接收方断开连接。
如果使用dispatch_uid标识接收者,则可能为None。