Django Signals使用

定義Signals

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處理函數

Signal處理函數是一個函數或者是一個實例的方法,而且必須知足下面條件:spa

  1. hashable
  2. 能夠接收關鍵詞參數

其中處理函數必須包含的關鍵詞參數有兩個:線程

  • signal,要接收的Signal實例
  • sender,要接收的Signal觸發者
# 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)

處理函數綁定Signal

django提供了兩種方法能夠將Signal的處理函數和Signal實例進行綁定:code

  • 手動調用connect方法
  • 使用裝飾器receiver

其實裝飾器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

發送Singal

有了前面定義的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)
        
        ...
相關文章
相關標籤/搜索