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内置的信号:

 

  1. Model signals

    • pre_init
    • post_init
    • pre_save
    • post_save
    • pre_delete
    • post_delete
    • class_prepared
  2. Management signals

    • pre_syncdb
    • post_syncdb
    • pre_migrate
    • post_migrate
       
  3. Request/response signals

    • request_started
    • request_finished
    • got_request_exception
  4. 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)

 

--------EOF---------
微信分享/微信扫码阅读