Django信号机制
我之前在Flask的文章中已经介绍了Flask的信号机制:Flask信号机制。
其实Django的信号机制几乎和Flask的信号机制完全一样,都是采用“发布-订阅”的模式,只是在信号的使用上有所不同。
Flask采用blinker提供信号支持;Django有自己内置的Signal类。两者只是在用法上不同,但逻辑上的确是有异曲同工之妙。
Django使用内置的Signal类,其在django.core.dispatcher中。
class Signal(object):
"""
Base class for all signals
Internal attributes:
receivers
{ receiverkey (id) : weakref(receiver) }
"""
def __init__(self, providing_args=None, use_caching=False):
"""
信号肯定要有发送者和订阅者:
先定义一个信号:
test_signal = Signal(providing_args=['a','b']))
#包含两个参数,是发送者要发送给订阅者的参数。
发送信号:
Django中有两种方法用于发送信号。
- Signal.send(sender, **kwargs)¶
- Signal.send_robust(sender, **kwargs)¶
调用 Signal.send()或者Signal.send_robust()来发送信号。你必须提供sender 参数(大多数情况下它是一个类),并且可以提供尽可能多的关键字参数。具体区别可参考官网:Django信号
test_signal.send(sender='test_signal',a=1,b=2)
订阅信号:
原型:
Signal.connect(receiver[, sender=None, weak=True, dispatch_uid=None])¶
Parameters:
receiver – 和这个信号连接的回调函数。详见Receiver 函数。
sender – 指定一个特定的sender,来从它那里接受信号。详见连接由指定sender发送的信号。
weak – Django 通常以弱引用储存信号处理器。这就是说,如果你的receiver 是个局部变量,可能会被垃圾回收。当你调用信号的connect()方法时,传递 weak=False来防止这样做。
dispatch_uid – 一个信号receiver 的唯一标识符,以防信号多次发送。详见防止重复的信号。
def add(sender,**kwargs):
for item in kwargs:
print item
test_signal.connect(add,sender=test_signal)
为了防止重复信号,即一个订阅者的接收器函数被多次执行,可以加上一个参数dispatch_uid,该参数的作用是给你的接收着加上一个唯一的标识。
Django订阅信号同样也是用connect。j接收器函数必须得有sender参数,另外connect不一定要带sender,不带sender意思就是不用特指接收那个发送者的信号。
除了上面直接用connect的方法订阅信号,还可以用Django提供的装饰器receiver.官方例子:
from django.core.signals import request_finished
from django.dispatch import receiver
@receiver(request_finished)
def my_callback(sender, **kwargs):
print("Request finished!")
这个装饰器肯定是也要调用connect方法的,具体看一下该装饰器的原型:
ef receiver(signal, **kwargs):
"""
A decorator for connecting receivers to signals. Used by passing in the
signal (or list of signals) and keyword arguments to connect::
@receiver(post_save, sender=MyModel)
def signal_receiver(sender, **kwargs):
...
@receiver([post_save, post_delete], sender=MyModel)
def signals_receiver(sender, **kwargs):
...
"""
def _decorator(func):
if isinstance(signal, (list, tuple)):
for s in signal:
s.connect(func, **kwargs)
else:
signal.connect(func, **kwargs)
return func
return _decorator
上面展示了Django信号的基本使用情况。再说下Django内置的信号:
-
Model signals
- pre_init
- post_init
- pre_save
- post_save
- pre_delete
- post_delete
- class_prepared
-
Management signals
- pre_syncdb
- post_syncdb
- pre_migrate
- post_migrate
-
Request/response signals
- request_started
- request_finished
- got_request_exception
-
Test signals
- django.test.signals.setting_changed
上面有很多在Flask中也有使用,比如request/response的信号。
我就拿Django源码中的实例来说一下。Django在每次出发请求之前,都会由信号request_started发送信号,你可以自定义一些函数或者类去执行,使用receiver装饰器即可。下面是源码:
class WSGIHandler(base.BaseHandler):
initLock = Lock()
request_class = WSGIRequest
def __call__(self, environ, start_response):
# Set up middleware if needed. We couldn't do this earlier, because
set_script_prefix(get_script_name(environ))
signals.request_started.send(sender=self.__class__, environ=environ)
try:
request = self.request_class(environ)
微信分享/微信扫码阅读