django 1.8 官方文档翻译:14-5 信号

简介: 信号Django包含一个“信号的分发器”,允许解耦的应用在信号出现在框架的任何地方时,都能获得通知。简单来说,信号允许指定的 发送器通知一系列的接收器,一些操作已经发生了。

信号

Django包含一个“信号的分发器”,允许解耦的应用在信号出现在框架的任何地方时,都能获得通知。简单来说,信号允许指定的 发送器通知一系列的接收器,一些操作已经发生了。当一些代码会相同事件感兴趣时,会十分有帮助。

Django 提供了一系列的内建信号,允许用户的代码获得DJango的特定操作的通知。这包含一些有用的通知:

关于完整列表以及每个信号的完整解释,请见内建信号的文档

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

监听信号

你需要注册一个接收器函数来接受信号,它在信号使用Signal.connect()发送时被调用:

Signal.``connect(receiver[, sender=None, weak=True, dispatch_uid=None])

Parameters: * **receiver** – 和这个信号连接的回调函数。详见[_接收器函数_](#receiver-functions)。 * **sender** – 指定一个特定的发送器,来从它那里接受信号。详见[_连接由指定发送器发送的信号_](#connecting-to-specific-signals)。 * **weak** – DJango通常以弱引用储存信号处理器。这就是说,如果你的接收器是个局部变量,可能会被垃圾回收。当你调用信号的`connect()`方法是,传递 `weak=False`来防止这样做。 * **dispatch_uid** – 一个信号接收器的唯一标识符,以防信号多次发送。详见[_防止重复的信号_](#preventing-duplicate-signals)。

让我们来看一看它如何通过注册在每次在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)

或者使用receiver() 装饰器来自动连接:

receiver(signal)

Parameters: **signal** – A signal or a list of signals to connect a function to.

下面是使用装饰器连接的方法:

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子模块就可以了。

Changed in Django 1.7:

由于ready()并不在Django之前版本中存在,信号的注册通常在models模块中进行。

注意

ready() 方法会在测试期间执行多次,所以你可能想要防止重复的信号,尤其是打算在测试中发送它们的情况。

连接由指定发送器发送的信号

一些信号会发送多次,但是你只想接收这些信号的一个确定的子集。例如,考虑 django.db.models.signals.pre_save 信号,它在模型保存之前发送。大多数情况下,你并不需要知道 任何模型何时保存 – 只需要知道一个特定的模型何时保存。

在这些情况下,你可以通过注册来接收只由特定发送器发出的信号。对于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实例保存时被调用。

不同的信号使用不同的对象作为他们的发送器;对于每个特定信号的细节,你需要查看内建信号的文档

防止重复的信号

在一些情况下,向接收者发送信号的代码可能会执行多次。这会使你的接收器函数被注册多次,并且导致它对于同一信号事件被调用多次。

如果这样的行为会导致问题(例如在任何时候模型保存时使用信号来发送邮件),传递一个唯一的标识符作为 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 的实例。providing_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(object):
    ...

    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类的异常,并且确保所有接收器都能得到信号的通知。如果发生了错误,错误的实例会在产生错误的接收器的二元组中返回。

New in Django 1.8:

调用send_robust()的时候,所返回的错误的__traceback__属性上会带有 traceback。

断开信号

Signal.``disconnect([receiver=None, sender=None, weak=True, dispatch_uid=None])

调用Signal.disconnect()来断开信号的接收器。 Signal.connect()中描述了所有参数。如果接收器成功断开,返回 True ,否则返回False

receiver参数表示要断开的已注册接收器。如果dispatch_uid 用于定义接收器,可以为None

Changed in Django 1.8:

增加了返回的布尔值。

译者:Django 文档协作翻译小组,原文:Signals

本文以 CC BY-NC-SA 3.0 协议发布,转载请保留作者署名和文章出处。

Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。

相关文章
|
7月前
|
开发者 Python
Django的信号机制:实现应用间的通信与响应
【4月更文挑战第15天】Django信号机制实现跨组件通信,基于订阅/发布模式,允许在事件(如模型保存、删除)发生时触发自定义函数。内置信号如`pre_save`、`post_save`,也可自定义信号。使用包括定义信号、连接处理器和触发信号。常用于模型操作监听、第三方应用集成和跨应用通信。注意避免滥用和保证处理器健壮性。信号机制提升代码可维护性和扩展性。
|
调度 数据库 开发者
django -- 信号
django -- 信号
|
设计模式 Python
Django的信号机制详解
Django的信号机制详解
|
中间件 Python
django 1.8 官方文档翻译: 3-6-1 中间件概览
Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html 中间件 中间件是一个介入Django的请求和响应的处理过程中的钩子框架。
1057 4
|
缓存 中间件 数据库
第25天,Django之缓存、序列化、信号
目录 一、缓存 1. 配置 1.1 开发调试 1.2 内存 1.3 文件 1.4 数据库 1.
1021 0
|
数据库 Python 调度
Django中的信号及其用法
Django中提供了"信号调度",用于在框架执行操作时解耦. 一些动作发生的时候,系统会根据信号定义的函数执行相应的操作 Django中内置的signal Model_signals pre_init # Django中的model对象执行其构造方法前,...
795 0
|
前端开发 Python
django 1.8 官方文档翻译: 3-4-3 使用基于类的视图处理表单
使用基于类的视图处理表单 表单的处理通常有3 个步骤: 初始的的GET (空白或预填充的表单) 带有非法数据的POST(通常重新显示表单和错误信息) 带有合法数据的POST(处理数据并重定向) 你自己实现这些功能经常导致许多重复的样本代码(参见在视图中使用表单)。
939 0
|
Python
django 1.8 官方文档翻译: 4-2-4 人性化
Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html django.contrib.humanize 一系列Django的模板过滤器,有助于向数据添加“人文关怀”。
811 0
|
数据库 Python 开发者
django 1.8 官方文档翻译: 3-4-2 内建显示视图
Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html 基于类的内建通用视图 编写Web应用可能是单调的,因为你需要不断的重复某一种模式。
781 0
|
SQL 测试技术 数据库
django 1.8 官方文档翻译: 2-6-3 提供初始数据
Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html 为模型提供初始数据 当你首次建立一个应用的时候,为你的数据库预先安装一些硬编码的数据,是很有用处的。
1028 0