django自带一套信号机制来帮助我们在框架的不同位置之间传递信息。也就是说,当某一事件发生时,信号系统可以允许一个或多个发送者(senders)将通知或信号(signals)发送给一组接受者(receivers)。

信号系统包含以下三要素:

  • 发送者-信号的发出方

  • 信号-信号本身

  • 接收者-信号的接受者

Django内置了一整套信号,下面是一些比较常用的:

  • django.db.models.signals.pre_save & django.db.models.signals.post_save

在ORM模型的save()方法调用之前或之后发送信号

  • django.db.models.signals.pre_delete & django.db.models.signals.post_delete

在ORM模型或查询集的delete()方法调用之前或之后发送信号。

  • django.db.models.signals.m2m_changed

当多对多字段被修改时发送信号。

  • django.core.signals.request_started & django.core.signals.request_finished

当接收和关闭HTTP请求时发送信号。

一、监听信号

要接收信号,请使用Signal.connect()方法注册一个接收器。当信号发送后,会调用这个接收器。

方法原型:

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

参数:

receiver :当前信号连接的回调函数,也就是处理信号的函数。
sender :指定从哪个发送方接收信号。
weak : 是否弱引用
dispatch_uid :信号接收器的唯一标识符,以防信号多次发送。

下面以如何接收每次HTTP请求结束后发送的信号为例,连接到Django内置的现成的request_finished信号。

1. 编写接收器

接收器其实就是一个Python函数或者方法:

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

请注意,所有的接收器都必须接收一个sender参数和一个**kwargs通配符参数。

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!")

3. 接收特定发送者的信号

一个信号接收器,通常不需要接收所有的信号,只需要接收特定发送者发来的信号,所以需要在sender参数中,指定发送方。下面的例子,只接收MyModel模型的实例保存前的信号。

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实例保存时被调用。

4. 防止重复信号

为了防止重复信号,可以设置dispatch_uid参数来标识你的接收器,标识符通常是一个字符串,如下所示:

from django.core.signals import request_finished

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

最后的结果是,对于每个唯一的dispatch_uid值,你的接收器都只绑定到信号一次。

二、自定义信号

除了Django为我们提供的内置信号(比如前面列举的那些),很多时候,我们需要自己定义信号。

类原型:class Signal(providing_args=list)[source]

所有的信号都是django.dispatch.Signal的实例。providing_args参数是一个列表,由信号将提供给监听者的参数的名称组成。可以在任何时候修改providing_args参数列表。

下面定义了一个新信号:

import django.dispatch

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

上面的例子定义了pizza_done信号,它向接受者提供size和toppings 参数。

三、发送信号

Django中有两种方法用于发送信号。

Signal.send(sender, **kwargs)[source]

Signal.send_robust(sender,** kwargs)[source]

必须提供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), ... ],表示接收器和响应值二元元组的列表。

四、断开信号

方法:

Signal.disconnect(receiver=None, sender=None, dispatch_uid=None)[source]

Signal.disconnect()用来断开信号的接收器。和Signal.connect()中的参数相同。如果接收器成功断开,返回True,否则返回False。

五、信号使用实例

信号可能不太好理解,下面我在Django内编写一个例子示范一下:

首先在根URLCONF中写一条路由:

from django.conf.urls import url
from django.contrib import admin
from app1 import views urlpatterns = [
# url(r'^admin/', admin.site.urls),
url(r'^signal/$', views.create_signal),
]

这个很好理解,我在项目里创建了一个app1应用,在它的views.py中创建了一个create_signal视图,通过/signal/可以访问这个视图。这些都不重要,随便配置,只要能正常工作就行。

然后在views.py中自定义一个信号,以及创建create_signal视图:

from django.shortcuts import HttpResponse
import time
import django.dispatch
from django.dispatch import receiver # 定义一个信号
work_done = django.dispatch.Signal(providing_args=['path', 'time']) def create_signal(request):
url_path = request.path
print("我已经做完了工作。现在我发送一个信号出去,给那些指定的接收器。") # 发送信号,将请求的url地址和时间一并传递过去
work_done.send(create_signal, path=url_path, time=time.strftime("%Y-%m-%d %H:%M:%S"))
return HttpResponse("200,ok")

自定义的信号名叫work_done,它很简单,接收请求url地址和请求时间两个参数。

create_signal视图内,获取请求的url,生成请求的时间,作为参数,传递到send方法。

这样,我们就发送了一个信号。

然后,再写一个接收器:

@receiver(work_done, sender=create_signal)
def my_callback(sender, **kwargs):
print("我在%s时间收到来自%s的信号,请求url为%s" % (kwargs['time'], sender, kwargs["path"]))

通过装饰器注册为接收器。内部接收字典参数,并解析打印出来。

最终views.py文件如下:

from django.shortcuts import HttpResponse
import time
import django.dispatch
from django.dispatch import receiver # Create your views here. # 定义一个信号
work_done = django.dispatch.Signal(providing_args=['path', 'time']) def create_signal(request):
url_path = request.path
print("我已经做完了工作。现在我发送一个信号出去,给那些指定的接收器。") # 发送信号,将请求的IP地址和时间一并传递过去
work_done.send(create_signal, path=url_path, time=time.strftime("%Y-%m-%d %H:%M:%S"))
return HttpResponse("200,ok") @receiver(work_done, sender=create_signal)
def my_callback(sender, **kwargs):
print("我在%s时间收到来自%s的信号,请求url为%s" % (kwargs['time'], sender, kwargs["path"]))

现在可以来测试一下。python manage.py runserver启动服务器。浏览器中访问http://127.0.0.1:8000/signal/。重点不再浏览器的返回,而在后台返回的内容:

我已经做完了工作。现在我发送一个信号出去,给那些指定的接收器。
我在2017-12-18 17:10:12时间收到来自<function create_signal at 0x0000000003AFF840>的信号,请求url为/signal/

这些提示信息,可以在Pycharm中看到,或者在命令行环境中看到。

django信号 signal的更多相关文章

  1. Django 信号signal

    序言 Django自带一套信号机制来帮助我们在框架的不同应用位置之间传递信息.也就是说,当某一事件发生时,信号系统可以允许一个或多个发送者(senders)将信号(signals)发送给一组接收者(r ...

  2. Django 详解 信号Signal

    Django信号 Django中提供了“信号调度”,用于在框架执行操作时解耦.通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者. Model signals pre_init # ...

  3. 23:django 信号(signal)

    django包含了一个“信号分配器”使得当一些动作在框架的其他地方发生的时候,解耦的应用可以得到提醒.通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者,这是特别有用的设计因为有些 ...

  4. Django中信号signal针对model的使用

    Django中实现对数据库操作的记录除了使用[开源插件]还可以使用信号signal独立实现 信号机制-观察者模式-发布与订阅:signal - 配置 # 文件路径:Django/myapps/__in ...

  5. 第六章:Django 综合篇 - 8:信号 signal

    django自带一套信号机制来帮助我们在框架的不同位置之间传递信息.也就是说,当某一事件发生时,信号系统可以允许一个或多个发送者(senders)将通知或信号(signals)发送给一组接受者(rec ...

  6. pythonのdjango 信号

    一.内置信号 Django中提供了“信号调度”,用于在框架执行操作时解耦.通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者. Model signals pre_init # d ...

  7. django信号浅谈

    Django中提供了“信号调度”,用于在框架执行操作时解耦.通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者. 1.Django内置信号 Model signals pre_in ...

  8. django信号

    什么是信号? 信号是在某个操作前或后自动触发一些操作. 信号是通知,是一种状态,相当于在某种状态下发特定的消息 --为了实现代码层解耦 村长博客:http://www.cnblogs.com/legu ...

  9. Django 信号

    信号 Django 提供一个“信号分发器”,允许解耦的应用在框架的其它地方发生操作时会被通知到. 简单来说,信号允许特定的sender通知一组receiver某些操作已经发生. 这在多处代码和同一事件 ...

随机推荐

  1. Java中使用自定义类封装数组,添加类方法实现数据操作

    1.具体见注释 2.后续或有更新 public class MyArray { private long[] array; private int cnt; // 自定义数组类的元素个数 /** 使用 ...

  2. 在配置好环境以后,启动tomcat后,出现这个异常

    15-Apr-2019 16:48:13.299 严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.StandardConte ...

  3. JDK常用命令(一)jps、jstat

    曾几何时,我们学习java都不再研究jdk而直接使用IDEA.eclipse和Netbeans,仿佛我们就认为我们的程序是这些编辑器编译出来的,这时多么可笑.殊不知,编辑器就是方便我们编辑开发的,而真 ...

  4. ORA-12052: cannot fast refresh materialized view

    SQL> execute dbms_mview.refresh ('TX_FAIL_LOG_DAY_MV', 'f'); BEGIN DBMS_MVIEW.REFRESH ('TX_FAIL_L ...

  5. 20145208 蔡野 《网络对抗》Exp9 web安全基础实践

    20145208 蔡野 <网络对抗>Exp9 web安全基础实践 本实践的目标理解常用网络攻击技术的基本原理.Webgoat实践下相关实验. 实验后回答问题 (1)SQL注入攻击原理,如何 ...

  6. CodeForces 867B Save the problem

    B. Save the problem! http://codeforces.com/contest/867/problem/B time limit per test 2 seconds memor ...

  7. Gym 101775J Straight Master(差分数组)题解

    题意:给你n个高度,再给你1~n每种高度的数量,已知高度连续的3~5个能消去,问你所给的情况能否全部消去:例:n = 4,给出序列1 2 2 1表示高度1的1个,高度2的2个,高度3的2个,高度4的1 ...

  8. BZOJ4018: 小Q的幻想之乡

    Description 背景 有一天,小Q梦见自己来到了理想国的幻想之乡. 描述 有一天,小Q梦见自己来到了理想国的幻想之乡.幻想乡有无穷户居民,第i个家庭住在编号为i的房屋里,编号从1开始,到正无穷 ...

  9. 数据库03_SQL语句

    由于在笔试中遇到写sql语句的题目,犯了低级错误,这里学习并总结一下,遇到相关的继续更新... 数据定义 1.创建数据库 create database testdb; 2.创建空表 需要指明表明.字 ...

  10. BZOJ4455 小星星

    闲扯 看到多个限制条件的计数题目,就想到容斥原理 思路 题目要求两个条件 - 编号一一对应 - 树上存在的边,在图上映射到的点上也应该存在 考虑一个暴力的dp,设\(dp_{i,j}\)表示i点编号对 ...