Django自身提供了一些常见的signal,用户自己也能够定义本身须要的signaldjango
定义signal很简单,只须要实例化一个Signal实例便可app
实例化Signal时,能够传入关键词参数providing_args, providing_args是一个列表,列表中定义了当前signal调用send方法时能够传入的参数。函数
# django.core.signals.py from django.dispatch import Signal request_started = Signal(providing_args=["environ"]) request_finished = Signal() got_request_exception = Signal(providing_args=["request"]) setting_changed = Signal(providing_args=["setting", "value", "enter"])
其中Signal的初始化也比较简单,就是为实例化的signal定义一个线程锁ui
class Signal(object): def __init__(self, providing_args=None, use_caching=False): self.receivers = [] if providing_args is None: providing_args = [] self.providing_args = set(providing_args) self.lock = threading.Lock() self.use_caching = use_caching # For convenience we create empty caches even if they are not used. # A note about caching: if use_caching is defined, then for each # distinct sender we cache the receivers that sender has in # 'sender_receivers_cache'. The cache is cleaned when .connect() or # .disconnect() is called and populated on send(). self.sender_receivers_cache = weakref.WeakKeyDictionary() if use_caching else {} self._dead_receivers = False
Signal处理函数是一个函数或者是一个实例的方法,而且必须知足下面条件:spa
其中处理函数必须包含的关键词参数有两个:线程
# django.db.__init__.py from django.core import signals from django.db.utils import ConnectionHandler connections = ConnectionHandler() def reset_queries(**kwargs): for conn in connections.all(): conn.queries_log.clear() signals.request_started.connect(reset_queries) def close_old_connections(**kwargs): for conn in connections.all(): conn.close_if_unusable_or_obsolete() signals.request_started.connect(close_old_connections) signals.request_finished.connect(close_old_connections)
django提供了两种方法能够将Signal的处理函数和Signal实例进行绑定:code
其实装饰器receiver最终仍是调用了connect方法将处理函数和Signal实例进行绑定rem
Signal类的connect方法定义以下:hash
class Signal(object): ... def connect(self, receiver, sender=None, weak=True, dispatch_uid=None): from django.conf import settings # If DEBUG is on, check that we got a good receiver if settings.configured and settings.DEBUG: assert callable(receiver), "Signal receivers must be callable." # Check for **kwargs if not func_accepts_kwargs(receiver): raise ValueError("Signal receivers must accept keyword arguments (**kwargs).") if dispatch_uid: lookup_key = (dispatch_uid, _make_id(sender)) else: lookup_key = (_make_id(receiver), _make_id(sender)) if weak: ref = weakref.ref receiver_object = receiver # Check for bound methods if hasattr(receiver, '__self__') and hasattr(receiver, '__func__'): ref = WeakMethod receiver_object = receiver.__self__ if six.PY3: receiver = ref(receiver) weakref.finalize(receiver_object, self._remove_receiver) else: receiver = ref(receiver, self._remove_receiver) with self.lock: self._clear_dead_receivers() for r_key, _ in self.receivers: if r_key == lookup_key: break else: self.receivers.append((lookup_key, receiver)) self.sender_receivers_cache.clear()
每一个信号量根据receiver和sender均可以获取一个lookup_key能够惟一的标志一个Signal和其处理方法, 当调用Signal实例的connect方法时,会判断绑定的处理函数是否已经在自身receivers中,若是存在则不会重复注册it
有了前面定义的Signal实例,以及定义的Signal实例处理方法,通过处理函数绑定Signal实例后就能够在必要的地方发送信号, 而后让绑定的处理函数处理了。
# django.core.handlers.wsgi.py from threading import Lock from django.core import signals from django.core.handlers import base class WSGIHandler(base.BaseHandler): ... def __call__(self, environ, start_response): ... signals.request_started.send(sender=self.__class__, environ=environ) ...