在真实的企业生产环境中,咱们会遇到各类各样的需求,好比对客户端请求进行过滤,将知足某些条件的客户端请求过滤掉,这时咱们能够利用Django的中间件来实现该需求;或者,我但愿每次model的save()方法被调用先后,都要写一条日志到日志文件中,而此时咱们能够经过Django提供的内置信号post_save来实现,本文介绍一下Django的高级功能——信号。html
“信号分发器”容许解耦的应用在框架的其它地方发生操做时会被通知到。 简单来讲,信号容许特定的sender通知一组receiver某些操做已经发生。 这在多处代码和同一事件有关联的状况下颇有用。shell
Django2.1官文对signal的解释以下: 数据库
Django includes a "signal dispatcher" which helps allow decoupled applications get notified when actions occur elsewhere in the framework.
In a nutshell, signals allow certain senders to notify a set of receivers that some action has taken place.
They're especially useful when many pieces of code may be interested in the same events.
翻译过来就是:Django框架内部包含了一个信号调度器,它的做用是能够将框架内部发生的任何操做都通知到功能独立的应用程序,固然,咱们也能够缩小发送者和接收者的范围,即指定具体的发送者和接受者,假设咱们的程序中有多个业务逻辑都在等待某一个事件发生以后再继续执行后面的代码,那么此时,信号是很是有用的。django
Django内置的信号以下(加粗的是比较经常使用的):浏览器
Model signals pre_init # django的model执行其构造方法前,自动触发 post_init # django的model执行其构造方法后,自动触发 pre_save # django的model对象保存前,自动触发 post_save # django的model对象保存后,自动触发 pre_delete # django的model对象删除前,自动触发 post_delete # django的model对象删除后,自动触发 m2m_changed # django的model中使用m2m字段操做第三张表(add,remove,clear)先后,自动触发 class_prepared # 程序启动时,检测已注册的app中modal类,对于每个类,自动触发 Management signals pre_migrate # 执行migrate命令前,自动触发 post_migrate # 执行migrate命令后,自动触发 Request/response signals request_started # 请求到来前,自动触发 request_finished # 请求结束后,自动触发 got_request_exception # 请求异常后,自动触发 Test signals setting_changed # 使用test测试修改配置文件时,自动触发 template_rendered # 使用test测试渲染模板时,自动触发 Database Wrappers connection_created # 建立数据库链接时,自动触发
对于Django内置的信号,仅需注册指定信号,当程序执行相应操做时,自动触发注册函数。注册信号,写入与project同名的文件夹下的_init_.py文件中,也是换数据库引擎的地方。app
如上图,项目名为signalTest,咱们在与项目同名的包的__init__.py文件中注册Django内置的信号,而后使用就能够了。框架
方式一:直接监听函数
def my_callback(sender, **kwargs): print("Request finished!")
from django.core.signals import request_finished request_finished.connect(my_callback)
方式二:使用装饰器监听post
def my_callback(sender, **kwargs): print("Request finished!") from django.core.signals import request_finished from django.dispatch import receiver @receiver(request_finished) def my_callback(sender, **kwargs): print("Request finished!")
from django.db.models.signals import pre_save from django.dispatch import receiver from myapp.models import MyModel # 在MyModel对应的数据保存前执行my_handler函数 @receiver(pre_save, sender=MyModel) def my_handler(sender, **kwargs): xxx
注册完内置信号后,只要知足触发条件,对应的函数会在相应的条件下去执行,不用人手动去调用。 测试
更多内置信号的说明请参考这篇博客:http://www.javashuo.com/article/p-engwidfj-bc.html
首先要知道的是,咱们用的信号都是django.dispatch.Signal这个类的实例。
在任意的py文件中定义信号:
import django.dispatch signal_done = django.dispatch.Signal(providing_args=["height", "width"])
def callback(sender, **kwargs): print("callback") print(sender,kwargs) signal_done.connect(callback)
from 路径 import signal_done signal_done.send(sender='Naruto',height=123, width=456)
最后须要注意:因为内置信号的触发者已经集成到Django中,因此其会自动调用;而对于自定义信号则须要开发者在对应位置指定触发。
上面介绍的一种方式是在项目同名的包的__init__.py文件中去注册信号。
咱们也能够利用Django在路由分发以前作一下信号的注册操做。
这里用到了Django启动的机制:django.dispatch.Signal在django.setup()的过程当中,它会遍历settings.INSTALLED_APPS列表中的每一项,并调用该AppConfig的ready方法,所以,将recevier订阅signal的过程放置于ready方法中就能保证该代码的执行。
咱们来拿一个具体的项目为例。
1、项目的目录结构以下
(1)把全部的信号都写在了signals包中,而且signals包中的__init__.py文件中实例化Signal类的对象(注意Python在import一个包的时候会执行里面的__init__文件),执行的操做我写在了handlers.py文件中;
(2)而后,利用Django的启动的原理,我把信号的注册写在了apps.py的SignalappConfig类的ready方法中,保证在路由分发以前就注册自定义的信号。
2、注册信号的具体写法
signals/__init__.py:
# -*- coding:utf-8 -*- from django.dispatch import Signal my_signal = Signal(providing_args=[])
signals/handlers.py:
# -*- coding:utf-8 -*- def my_callback(sender,**kwargs): print('my_callback...')
signalapp/apps.py
from django.apps import AppConfig # 从外部导入自定义信号及处理的函数 from signals import my_signal from signals.handlers import my_callback class SignalappConfig(AppConfig): name = 'signalapp' def ready(self): # 注册信号 my_signal.connect(my_callback)
3、使用自定义的信号
作一个简单的路由与视图测试一下这个自定义的信号是否成功:
signalTest/urls.py:
from django.contrib import admin from django.urls import path from signalapp import views urlpatterns = [ path('admin/', admin.site.urls), path('index/',views.index,name='index'), ]
sjgnalapp/views.py:
from django.shortcuts import render,HttpResponse # 导入自定义信号的处理函数 from signals.handlers import my_callback def index(request): # 使用自定义信号 my_callback(sender='index') return HttpResponse('OK')
启动Django程序后咱们在浏览器中输入127.0.0.1:8000/index,能够看到在后台打印出了自定义信号处理函数中所打印的数据:
my_callback...
默认状况下,某些信号会被屡次发送,可是,一般,咱们只但愿接收某个或者某些特定的发送者发出的信号,好比说django.db.models.signals.pre_saves,该信号,它在每一个model的save()方法被执行的时候被发送,不过,不少状况下,咱们只想记录某个特定的model的save()方法被执行时的日志。
在上述状况下,咱们能够指定只接受咱们指定的信号发送者发出的信号。
仍是用django.db.models.signals.pre_saves举例,下面咱们来演示如何指定发送者
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): xxx
若是不但愿再接收某个信号,咱们能够调用Signal.disconnect()方法。