您好,登錄后才能下訂單哦!
定義Signals
Django自身提供了一些常見的signal,用戶本身也可以定義自己需要的signal
定義signal很簡單,只需要實例化一個Signal實例即可
實例化Signal時,可以傳入關(guān)鍵詞參數(shù)providing_args, providing_args是一個列表,列表中定義了當前signal調(diào)用send方法時可以傳入的參數(shù)。
# 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定義一個線程鎖
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處理函數(shù)
Signal處理函數(shù)是一個函數(shù)或者是一個實例的方法,并且必須滿足下面條件:
其中處理函數(shù)必須包含的關(guān)鍵詞參數(shù)有兩個:
# 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)
處理函數(shù)綁定Signal
django提供了兩種方法可以將Signal的處理函數(shù)和Signal實例進行綁定:
其實裝飾器receiver最終還是調(diào)用了connect方法將處理函數(shù)和Signal實例進行綁定
Signal類的connect方法定義如下:
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()
每個信號量根據(jù)receiver和sender都可以獲取一個lookup_key可以唯一的標志一個Signal和其處理方法, 當調(diào)用Signal實例的connect方法時,會判斷綁定的處理函數(shù)是否已經(jīng)在自身receivers中,如果存在則不會重復(fù)注冊
發(fā)送Singal
有了前面定義的Signal實例,以及定義的Signal實例處理方法,經(jīng)過處理函數(shù)綁定Signal實例后就可以在必要的地方發(fā)送信號, 然后讓綁定的處理函數(shù)處理了。
# 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) ...
信號量最為Django的一個核心知識點,在項目中很少有使用到,所以很多人都不了解或者沒聽過過(包括我)。簡單來說就是在進行一些操作的前后我們可以發(fā)出一個信號來獲得特定的操作,這些操作包括
django.db.models.signals.pre_save&django.db.models.signals.post_save
在模型 save()方法調(diào)用之前或之后發(fā)送。
django.db.models.signals.pre_delete&django.db.models.signals.post_delete
在模型delete()方法或查詢集的delete() 方法調(diào)用之前或之后發(fā)送。
django.db.models.signals.m2m_changed
模型上的 ManyToManyField 修改時發(fā)送。
django.core.signals.request_started&django.core.signals.request_finished
Django開始或完成HTTP請求時發(fā)送。
其他細致的知識點,大家可以點鏈接查看,直接通過一個例子解釋:
在自定義用戶模型類的時候,在后臺添加用戶數(shù)據(jù)因為使用了自定義模型類的create所以密碼會以明文保存,接下來使用信號量方式在保存后馬上修改密碼解決。(網(wǎng)上一個項目的例子)
users/signals.py
from django.db.models.signals import post_save from django.dispatch import receiver from django.contrib.auth import get_user_model User = get_user_model() # post_save:上面七大方法之一:在模型保存之后的操作 # sender: 發(fā)出信號的model @receiver(post_save, sender=User) def create_user(sender, instance=None, created=False, **kwargs): """ sender:模型類。 instance:保存的實際實例。 created:如果創(chuàng)建了新記錄True。 update_fields:Model.save()要更新的字段集,如果沒有傳遞則為None """ if created: password = instance.password # instance相當于user instance.set_password(password) instance.save()
users/apps.py
from django.apps import AppConfig class UsersConfig(AppConfig): name = 'users' verbose_name = '用戶管理' def ready(self): """使用ready加載,否則不生效""" import users.signals
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。