深入理解Django-Signals信號量
定義Signals
Django自身提供了一些常見的signal,用戶本身也可以定義自己需要的signal
定義signal很簡單,只需要實(shí)例化一個(gè)Signal實(shí)例即可
實(shí)例化Signal時(shí),可以傳入關(guān)鍵詞參數(shù)providing_args, providing_args是一個(gè)列表,列表中定義了當(dāng)前signal調(diào)用send方法時(shí)可以傳入的參數(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的初始化也比較簡單,就是為實(shí)例化的signal定義一個(gè)線程鎖
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ù)是一個(gè)函數(shù)或者是一個(gè)實(shí)例的方法,并且必須滿足下面條件:
- hashable
- 可以接收關(guān)鍵詞參數(shù)
其中處理函數(shù)必須包含的關(guān)鍵詞參數(shù)有兩個(gè):
- signal,要接收的Signal實(shí)例
- sender,要接收的Signal觸發(fā)者
# 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實(shí)例進(jìn)行綁定:
- 手動(dòng)調(diào)用connect方法
- 使用裝飾器receiver
其實(shí)裝飾器receiver最終還是調(diào)用了connect方法將處理函數(shù)和Signal實(shí)例進(jìn)行綁定
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()
每個(gè)信號量根據(jù)receiver和sender都可以獲取一個(gè)lookup_key可以唯一的標(biāo)志一個(gè)Signal和其處理方法, 當(dāng)調(diào)用Signal實(shí)例的connect方法時(shí),會(huì)判斷綁定的處理函數(shù)是否已經(jīng)在自身receivers中,如果存在則不會(huì)重復(fù)注冊
發(fā)送Singal
有了前面定義的Signal實(shí)例,以及定義的Signal實(shí)例處理方法,經(jīng)過處理函數(shù)綁定Signal實(shí)例后就可以在必要的地方發(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的一個(gè)核心知識點(diǎn),在項(xiàng)目中很少有使用到,所以很多人都不了解或者沒聽過過(包括我)。簡單來說就是在進(jìn)行一些操作的前后我們可以發(fā)出一個(gè)信號來獲得特定的操作,這些操作包括
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 修改時(shí)發(fā)送。
django.core.signals.request_started&django.core.signals.request_finished
Django開始或完成HTTP請求時(shí)發(fā)送。
其他細(xì)致的知識點(diǎn),大家可以點(diǎn)鏈接查看,直接通過一個(gè)例子解釋:
在自定義用戶模型類的時(shí)候,在后臺添加用戶數(shù)據(jù)因?yàn)槭褂昧俗远x模型類的create所以密碼會(huì)以明文保存,接下來使用信號量方式在保存后馬上修改密碼解決。(網(wǎng)上一個(gè)項(xià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:保存的實(shí)際實(shí)例。
created:如果創(chuàng)建了新記錄True。
update_fields:Model.save()要更新的字段集,如果沒有傳遞則為None
"""
if created:
password = instance.password
# instance相當(dāng)于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í)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
python接口自動(dòng)化(十六)--參數(shù)關(guān)聯(lián)接口后傳(詳解)
這篇文章主要介紹了python接口自動(dòng)化參數(shù)關(guān)聯(lián)接口,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-04-04
python機(jī)器學(xué)習(xí)基礎(chǔ)線性回歸與嶺回歸算法詳解
這篇文章主要為大家介紹了python機(jī)器學(xué)習(xí)基礎(chǔ)線性回歸與嶺回歸算法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-11-11
Python使用Spire.PDF實(shí)現(xiàn)為PDF添加水印
在現(xiàn)代數(shù)字化辦公環(huán)境中,PDF 已成為一種廣泛使用的文件格式,尤其是在需要保持文檔格式時(shí),下面我們就來看看如何使用 Python 為 PDF 文件添加水印吧2025-12-12
Python實(shí)現(xiàn)的多進(jìn)程和多線程功能示例
這篇文章主要介紹了Python實(shí)現(xiàn)的多進(jìn)程和多線程功能,結(jié)合實(shí)例形式分析了Python多線程與多進(jìn)程實(shí)現(xiàn)分布式系統(tǒng)功能相關(guān)操作技巧,需要的朋友可以參考下2018-05-05
Python 類變量和實(shí)例變量的實(shí)現(xiàn)與區(qū)別(附示例)
本文主要介紹了Python 類變量和實(shí)例變量的實(shí)現(xiàn)與區(qū)別,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-03-03
python中threading.Semaphore和threading.Lock的具體使用
python中的多線程是一個(gè)非常重要的知識點(diǎn),本文主要介紹了python中threading.Semaphore和threading.Lock的具體使用,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2023-08-08
解決python3報(bào)錯(cuò)之takes?1?positional?argument?but?2?were?gi
這篇文章主要介紹了解決python3報(bào)錯(cuò)之takes?1?positional?argument?but?2?were?given問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03

