第六章:Django 综合篇 - 8:信号 signal
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 综合篇 - 8:信号 signal的更多相关文章
- Django 详解 信号Signal
Django信号 Django中提供了“信号调度”,用于在框架执行操作时解耦.通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者. Model signals pre_init # ...
- 第六章Django
web应用程序 server端建立socket,不断地accept,当收到客户端连接信号之后,服务端向客户端发送数据,将html网页打开,read出来,并发送至客户端,这样客户端就可以浏览到网页的内容 ...
- 《Django By Example》第六章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:无他,祝大家年会都中奖!) 第六章 ...
- Django 信号signal
序言 Django自带一套信号机制来帮助我们在框架的不同应用位置之间传递信息.也就是说,当某一事件发生时,信号系统可以允许一个或多个发送者(senders)将信号(signals)发送给一组接收者(r ...
- 十六 Django框架,信号
Django中提供了“信号调度”,用于在框架执行操作时解耦.通俗来讲,就是一些动作发生的时候,信号允许特定的发送者去提醒一些接受者. 也就是当程序有指定动作时,触发一个信号函数 1.Django内置信 ...
- Django中信号signal针对model的使用
Django中实现对数据库操作的记录除了使用[开源插件]还可以使用信号signal独立实现 信号机制-观察者模式-发布与订阅:signal - 配置 # 文件路径:Django/myapps/__in ...
- Linux进程间通信(一): 信号 signal()、sigaction()
一.什么是信号 用过Windows的我们都知道,当我们无法正常结束一个程序时,可以用任务管理器强制结束这个进程,但这其实是怎么实现的呢?同样的功能在Linux上是通过生成信号和捕获信号来实现的,运行中 ...
- 第二十二章 Django会话与表单验证
第二十二章 Django会话与表单验证 第一课 模板回顾 1.基本操作 def func(req): return render(req,'index.html',{'val':[1,2,3...]} ...
- Python Django缓存,信号,序列化,文件上传,Ajax登录和csrf_token验证
本节内容 models操作 Django的缓存 请求方式 序列化 Form 配合Ajax实现登录认证 上传文件 Ajax csrf_token验证方式 1 models操作 单表查询: curd(增 ...
- Django 2.0 学习(18):Django 缓存、信号和extra
Django 缓存.信号和extra Django 缓存 由于Django是动态网站,所以每次请求均会去数据库进行相应的操作,当程序访问量大时,耗时必然会显著增加.最简单的解决方法是:使用缓存,缓存将 ...
随机推荐
- CMU15445 (Fall 2019) 之 Project#3 - Query Execution 详解
前言 经过前面两个实验的铺垫,终于到了给数据库系统添加执行查询计划功能的时候了.给定一条 SQL 语句,我们可以将其中的操作符组织为一棵树,树中的每一个父节点都能从子节点获取 tuple 并处理成操作 ...
- Collection集合汇总
Collectioin(java) Collection简介 打开帮助文档 java.utill //使用时需要导包 Interface Collection 集合层次结构中的根界面 . 集合表示一组 ...
- 作业一、安装Ubuntu系统
Ubuntu1804安装 一.安装环境 1.VMware Workstation 16 Pro 2.Ubuntu 18.04.6 LTS 二.部署系统 步骤1.进入VMware,点击创建新的虚拟机 步 ...
- tokitsukaze and Soldier 来源:牛客网
题目 链接:https://ac.nowcoder.com/acm/contest/28886/1004 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 524288K, ...
- java 桥接方法
1.桥接方法简介 桥接方法是jdk1.5引入泛型后,为使java泛型方法生成的字节码与jdk1.5版本之前的字节码兼容由编译器自动生成的. 可用method.isBridge() 判断method是否 ...
- Ubuntu添加非root用户到Docker用户组
前言 首先平常公司的Linux生产环境为了防止误操作导致灾难性问题,一般都不会给我们开发开放root管理员的账号权限.所以平常在Ubuntu的普通用户登录的时候,要操作Dcoker一般都需要带上sud ...
- Luogu2343 宝石管理系统(平衡树)
平衡树维护总第K大:插入 #include <iostream> #include <cstdio> #include <cstring> #include < ...
- Luogu5367 【模板】康托展开 (康拓展开)
\(n^2\)暴力 #include <iostream> #include <cstdio> #include <cstring> #include <al ...
- java学习第一天.day05
jvm的内存 栈:类方法使用后自动销毁,销毁的好处是释放内存 java方法执行时,在栈区执行 堆: 线程共享的一块内存区域 所有的对象实例以及 数组 都要在堆上分配 每次使用new ...
- PerfView专题 (第六篇):如何洞察 C# 中 GC 的变化
一:背景 在洞察 GC 方面,我觉得市面上没有任何一款工具可以和 PerfView 相提并论,这也是为什么我会在 WinDbg 之外还要学习这么一款工具的原因,这篇我们先简单聊聊 PerfView 到 ...