深入理解Django的信號(hào)機(jī)制
Django的信號(hào)
Django的信號(hào)機(jī)制不同于Linux的信號(hào)機(jī)制,Django 中的信號(hào)用于在框架執(zhí)行操作時(shí)解耦。當(dāng)某些動(dòng)作發(fā)生的時(shí)候,系統(tǒng)會(huì)根據(jù)信號(hào)定義的函數(shù)執(zhí)行相應(yīng)的操作
Django的信號(hào)主要包含以下三個(gè)要素:
- 發(fā)送者(sender):信號(hào)的發(fā)出方。
- 信號(hào)(signal):發(fā)送的信號(hào)本身。
- 接收者(receiver):信號(hào)的接收者。
其中接受者就是回調(diào)函數(shù),會(huì)把這個(gè)函數(shù)注冊(cè)到信號(hào)之上。當(dāng)特定事件發(fā)生之后,發(fā)送者發(fā)送信號(hào),然后執(zhí)行回調(diào)函數(shù)。
Django信號(hào)的使用
查看Django Signal的源碼,看到Django的信號(hào)存在以下方法
除了上面的幾個(gè)方法,還有幾個(gè)屬性
- lock
- recievers
- sender_receivers_cache
- use_caching
根據(jù)第一趴介紹知道,要使用Django的信號(hào),需要滿足三要素(發(fā)送者、接受者、和信號(hào))
Django通過(guò)
connect()
函數(shù)監(jiān)聽(tīng)
信號(hào),接受發(fā)送者發(fā)送的信號(hào),然后執(zhí)行接受者(回調(diào)函數(shù))
Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None)
其中
- recievers 是接受者,本質(zhì)就是一個(gè)回調(diào)函數(shù)
- sender 是發(fā)送信號(hào)的主體,如果connect連接的時(shí)候,sender 是None(默認(rèn)也是None)代表該信號(hào)接受所有發(fā)送者發(fā)送的該信號(hào);否則只接受具體的發(fā)送者(一個(gè)Python對(duì)象,比如Django的 Model對(duì)象)
- weak – Django 默認(rèn)將信號(hào)處理程序存儲(chǔ)為弱引用。因此,如果你的接收器是本地函數(shù),則可能會(huì)對(duì)其進(jìn)行垃圾回收。要防止這種情況發(fā)生,當(dāng)你要調(diào)用 connect() 方法時(shí)請(qǐng)傳入 weak=False。
- dispatch_uid 在可能發(fā)送重復(fù)信號(hào)的情況下,信號(hào)接收器的唯一標(biāo)識(shí)符。
具體使用方式
1、需要定義一個(gè)回調(diào)函數(shù)
def my_callback(sender, **kwargs): print("Request finished!")
2、把回調(diào)函數(shù)
注冊(cè)到對(duì)應(yīng)的信號(hào)
from django.core.signals import request_finished request_finished.connect(my_callback)
注意這里沒(méi)有指定sender,那么就是接受任意發(fā)送者哦
或者更簡(jiǎn)單的方式是使用 receiver
函數(shù)
from django.core.signals import request_finished from django.dispatch import receiver @receiver(request_finished) def my_callback(sender, **kwargs): print("Request finished!")
或者指定具體的發(fā)送者
from django.core.signals import request_finished from django.dispatch import receiver @receiver(request_finished, sender="MyTagModel") def my_callback(sender, **kwargs): print("Request finished!")
==> 劃重點(diǎn)
一般我們會(huì)把 回調(diào)函數(shù)
和 信號(hào)注冊(cè)
放到一個(gè)應(yīng)用的目錄下的 signals.py 文件中去
然后在該應(yīng)用的 apps.py 中應(yīng)用
from django.apps import AppConfig from django.core.signals import request_finished class MyAppConfig(AppConfig): ... def ready(self): # Implicitly connect signal handlers decorated with @receiver. from . import signals # Explicitly connect a signal handler. request_finished.connect(signals.my_callback)
這樣只要改應(yīng)用安裝在 INSTALLED_APPS
中去,那么Django就能識(shí)別到具體的信號(hào)(包括自定義的信號(hào))以及進(jìn)行信號(hào)的處理(因?yàn)橐呀?jīng)自動(dòng)通過(guò) connect進(jìn)行監(jiān)聽(tīng) )
為啥會(huì)自動(dòng)監(jiān)聽(tīng)呢,當(dāng)然是 Django的 AppConfig
下的 ready() 函數(shù)的作用
自定義信號(hào)
首先有個(gè)核心點(diǎn)需要明確
所有的信號(hào)都是
django.dispatch.Signal
的實(shí)例。
比如這里新增一個(gè)發(fā)送郵件的信號(hào),每次新增Post之后,發(fā)送郵件給相關(guān)訂閱的人
# demoapp/signals.py from django.db.models.signals import post_save from django.dispatch import receiver from .models import Post @receiver(post_save, sender=Post) def send_mail(sender, instance, created, **kwargs): if created: print(f"current instance {instance}") print("Try to send mail to subscriber")
然后把信號(hào)導(dǎo)入到 appConfig 的ready() 函數(shù)中去
# demoapp/apps.py from django.apps import AppConfig class DemoappConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'demoapp' def ready(self): import demoapp.signals
然后我們?cè)赼dmin注冊(cè)Post之后,新增Post,在命令行日志中就能看到
current instance DemoPost First One
Try to send mail to subscriber
[02/Feb/2023 13:47:29] "POST /admin/demoapp/post/add/ HTTP/1.1" 302 0
還有另外一個(gè) 主動(dòng)發(fā)送信號(hào)
的方式
1、先定義回調(diào)函數(shù) callback_func
2、定義一個(gè)信號(hào) mail_send_signal = Signal()
3、回調(diào)函數(shù)注冊(cè)到信號(hào) mail_send_signal.connect(callback_func)
3、主動(dòng)發(fā)送信號(hào) mail_send_signal.send(sender=xxx, **kwargs)
具體的代碼實(shí)現(xiàn),可以手動(dòng)嘗試哦,實(shí)踐出真知嘛~
擴(kuò)展:查看Django信號(hào)的接受者
Django內(nèi)置信號(hào)的reciever查看
(kfzops) [ 23-02-02 16:04 ] [ colinspace.com ] python manage.py shell Python 3.9.6 (default, Jul 16 2021, 13:41:17) [Clang 12.0.5 (clang-1205.0.22.11)] on darwin Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from django.db.models.signals import post_migrate >>> post_migrate.receivers [(('django.contrib.auth.management.create_permissions', 4304618416), <weakref at 0x10232b310; to 'function' at 0x10231d1f0 (create_permissions)>), ((4331786592, 4304618416), <weakref at 0x102581e00; to 'function' at 0x10231d160 (create_contenttypes)>)]
擴(kuò)展:Django內(nèi)置信號(hào)
Django內(nèi)置了很多有用的信號(hào),大概有以下幾類,可以作為了解。
模型相關(guān)的信號(hào)
- pre_init
- post_init
- pre_save
- post_save
- pre_delete
- post_delete
- m2m_changed
- class_prepared
其中
1、pre_init/post_init 分別會(huì)在模型的__init__()
方法調(diào)用 之前/之后發(fā)出
2、pre_save/post_save 分別會(huì)在模型的save()
方法調(diào)用 之前/之后發(fā)出
3、pre_delete/post_delete 分別會(huì)在模型的delete()
方法調(diào)用 之前/之后發(fā)出
4、m2m_changed 嚴(yán)格意義上來(lái)說(shuō)是由ManyToManyField
字段發(fā)生變化的時(shí)候發(fā)出的
5、class_prepared 比較特殊一般不建議用
django-admin 發(fā)出的信號(hào),確切的說(shuō)是 Django admin在執(zhí)行 python manage.py migrate的時(shí)候發(fā)出的信號(hào)
- pre_migrete
- post_migrate
顧名思義,不做過(guò)多解釋
請(qǐng)求響應(yīng)信號(hào),也就是Django 發(fā)起request和響應(yīng)response的信號(hào)
- request_started
- request_finished
- get_request_exception
另外還有幾個(gè)特殊的信號(hào)
1、setting_changed 只有當(dāng)運(yùn)行測(cè)試用例的時(shí)候發(fā)出
2、template_render 當(dāng)測(cè)試系統(tǒng)渲染模板的時(shí)候發(fā)出
3、connect_created 當(dāng)數(shù)據(jù)庫(kù)連接啟動(dòng)的時(shí)候,數(shù)據(jù)庫(kù)管理器發(fā)出
參考文檔
1、 https://docs.djangoproject.com/zh-hans/4.1/topics/signals/
到此這篇關(guān)于深入理解Django的信號(hào)機(jī)制的文章就介紹到這了,更多相關(guān)Django 信號(hào)機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解Python實(shí)現(xiàn)多進(jìn)程異步事件驅(qū)動(dòng)引擎
本篇文章主要介紹了詳解Python實(shí)現(xiàn)多進(jìn)程異步事件驅(qū)動(dòng)引擎,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08詳解Python對(duì)某地區(qū)二手房房?jī)r(jià)數(shù)據(jù)分析
這篇文章主要為大家介紹了Python數(shù)據(jù)分析某地區(qū)二手房房?jī)r(jià),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2021-12-12Python求區(qū)間正整數(shù)內(nèi)所有素?cái)?shù)之和的方法實(shí)例
這篇文章主要給大家介紹了Python對(duì)區(qū)間正整數(shù)內(nèi)所有素?cái)?shù)之和的相關(guān)資料,文中介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10Python中不同進(jìn)制互相轉(zhuǎn)換(二進(jìn)制、八進(jìn)制、十進(jìn)制和十六進(jìn)制)
這篇文章主要介紹了Python中不同進(jìn)制互相轉(zhuǎn)換,本文講解了二進(jìn)制、八進(jìn)制、十進(jìn)制和十六進(jìn)制的相與轉(zhuǎn)換實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-04-04Python基于numpy靈活定義神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)的方法
這篇文章主要介紹了Python基于numpy靈活定義神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)的方法,結(jié)合實(shí)例形式分析了神經(jīng)網(wǎng)絡(luò)結(jié)構(gòu)的原理及Python具體實(shí)現(xiàn)方法,涉及Python使用numpy擴(kuò)展進(jìn)行數(shù)學(xué)運(yùn)算的相關(guān)操作技巧,需要的朋友可以參考下2017-08-08Python操作MySQL數(shù)據(jù)庫(kù)的簡(jiǎn)單步驟分享
這篇文章主要給大家介紹了關(guān)于Python操作MySQL數(shù)據(jù)庫(kù)的簡(jiǎn)單步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04