Django 信号详解
一、信号:
-
django自带一套
信号机制
来帮助我们在框架的不同位置之间传递信息。简单的说,当某一事件发生时,信号系统可以允许一个或多个发送者(senders)将通知或信号(siganls)发送给一组接收者(receivers)
-
信号系统包含的三要素:
1.发送者:信号的发出放
2.信号:信号本身
3.接收者:信号的接收者
1.1内置信号:
-
Django提供一组内置的信号,让代码可以通过Django自己通知的操作,得到一些有用的通知:
以下是model信号:
from django.db.models.signals import xxxxxx
model 信号 简介 pre_init model 执行其构造方法前,自动触发 post_init model 执行其构造方法后,自动触发 pre_save model 对象保存前,自动触发 post_save model 对象保存后,自动触发 pre_delete model 对象删除前,自动触发 post_delete model 对象删除后,自动触发 m2m_changed(m2m指的manytomany) m2m字段操作第三张表(add,remove,clear)前后,自动触发 class_prepared 程序启动时,检测已注册的app中modal类,对于每一个类,自动触发 以下是管理信号:
from django.db.models.signals import pre_migrate, post_migrate
管理信号 简介 pre_migrate 执行migrate命令前,自动触发 post_migrate 执行migrate命令后,自动触发 以下是请求返回信号:
from django.core.signals import xxxxxx
请求返回信号 简介 request_started 请求到来前,自动触发 request_finished 请求结束后,自动触发 got_request_exception 请求异常后,自动触发 以下是请求返回信号:
from django.test.signals impor xxxxxx
测试信号 简介 setting_changed 使用test测试修改配置文件时,自动触发 template_rendered 使用test测试渲染模板时,自动触发 以下是数据库信号:
from django.db.backends.signals import xxxxxx
数据库信号 简介 connection_created 创建数据库连接时,自动触发
二、监听信号:
-
要接收信号,使用
Signal.connect()
方法注册接收函数(接收器函数在信号发送时被调用)。Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None)
- 参数说明:
receiver
– 将被连接到这个信号的回调函数。sender
– 指定一个特定的发送者接收信号。weak
– Django 默认将信号处理程序存储为弱引用。因此,如果您的接收器是本地函数,则可能会被回收。为了防止这种情况发生,当你调用信号的connect()
方法时,传递weak=False
。dispatch_uid
– 在可能发送重复信号的情况下,信号接收器的唯一标识符。
- 参数说明:
2.1:接收器函数:
-
接收器可以是任何
python函数或方法
:def my_callback(sender, **kwargs): print("Request finished!")
-
参数说明:
-
sender
– 发送者(若是pre_save的话,就是model class) -
**kwargs
– 所有的信号都会发送关键字参数,并可能随时更改这些关键字参数。在request_finished的情况下,它被记录为不发送参数,就相当于: def my_callback(sender): pass 实际上这是错的,Django会抛出一个错误。 这是因为在任何时候参数都可能被添加到信号中,并且接收器必须能够处理这些新的参数。
-
-
2.2:连接接收器函数:
两种方法可以将接收器连接到信号。
-
手动连接路线:
from django.core.signals import request_finished request_finished.connect(my_callback)
-
使用装饰器
receiver()
: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
函数将在每次请求结束时被调用
2.3:连接到特定sender发送信号:
在 django.db.models.signals.pre_save
的情况下,sender 将是保存的模型类,因此您可以指示您只需要某个模型发送的信号:
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
函数。
三、自定义和发送信号:
3.1:自定义信号:
import django.dispatch
mark_question_deleted = django.dispatch.Signal(providing_args=["user_id"])
声明一个mark_question)deleted
信号,提供user_id
参数接收器。
3.2:发送信号:
有种发送信号方式:
Signal.send(sender, **kwargs)
Signal.send_robust(sender, **kwargs)
要发送信号,请调用 Signal.send()
(所有内置信号均使用此信号)或 Signal.send_robust()
。你必须提供 sender
参数(这是大多数时候的类),并且可以提供尽可能多的其他关键字参数。
-
举个栗子:使用
mark_question_deleted
信号:def send_reduce_user_attr_signals(report_type, body_obj): """减少用户发布/问题/回答/文章/评论的属性数量""" # 减少该用户的提问数 mark_question_deleted.send(sender=models.Question, user_id=body_obj.user_id)
send()
和send_robust()
都返回一组元组对[(receiver, response), ... ]
的列表,表示被调用的接收函数及其响应值的列表。send()
与send_robust()
的不同之处在于如何处理接收函数引发的异常。send()
不会捕获接收者引发的任何异常;它只是允许错误传播。因此,在出现错误时,并非所有接收机都可能被通知信号。send_robust()
捕获从 Python 的Exception
类派生的所有错误,并确保所有接收器都被通知信号。如果发生错误,则错误实例会返回到引发错误的接收方的元组对中。
3.3:断开信号:
Signal.disconnect(receiver=None, sender=None, dispatch_uid=None)
要从信号中断开接收器,请调用 Signal.disconnect()
。参数如 Signal.connect()
中所述。如果接收器断开,该方法返回 True
,否则返回 False
。
receiver
参数指示注册的接收者断开连接。如果使用 dispatch_uid
来标识接收者,它可能是 None
。
四、自定义信号使用实例:
-
如果上述代码不太好理解,下面简单举个栗子:
1.首先在 url.py 创建要访问的url
from django.conf.urls import url, include from . import views, signals urlpatterns = [ url(r'^signal/test/$', views.create_signal) ]
2.在它的views.py中创建了一个create_signal视图,通过
/signal/
可以访问这个视图from django.shortcuts import HttpResponse import time import django.dispatch from django.dispatch import receiver # 创建一个自定义信号(信号本身) # 自定义信号名就是work_done,接收请求的path、time 是两个参数 work_done = django.dispatch.Signal(providing_args=['path', 'time']) # 请求url地址,将访问这个FBV视图 def create_signal(request): # 获取请求的url url_path = request.path print('准备发送一个信号出去哦') # 发送者:信号的发出放 work_done.send(create_signal, path=url_path, time=time.strftime("%Y-%m-%d %H:%M:%S")) return HttpResponse('OK') # 接收者:信号的接收者(接收自定义信号名) @receiver(work_done, sender=create_signal) def my_callback(sender, **kwargs): print("我在%s时间收到来自%s的信号,请求url为%s" % (kwargs['time'], sender, kwargs["path"]))
3.启动Django服务器,访问:http://127.0.0.1:8000/signal/ ,输出内容:
准备发送一个信号出去哦 我在2019-06-06 16:23:09时间收到来自<function create_signal at 0x0000000005269048>的信号,请求url为/order/