Django 提供一個(gè)“信號(hào)分發(fā)器”,允許解耦的應(yīng)用在框架的其它地方發(fā)生操作時(shí)會(huì)被通知到。簡(jiǎn)單來(lái)說(shuō),信號(hào)允許若干 sender
(寄件人)通知一組 receiver
(接收者)某些操作已經(jīng)發(fā)生。這在多處代碼和同一事件有關(guān)聯(lián)的情況下很有用。
Django 提供一組內(nèi)建信號(hào)面哥,允許用戶的代碼獲得 Django 特定操作的通知。這包含一些有用的通知:
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 建立或關(guān)閉 HTTP 請(qǐng)求時(shí)發(fā)送尸红。
自定義信號(hào)
class Signal([providing_args=list])?
所有信號(hào)都是 django.dispatch.Signal 的實(shí)例吱涉。providing_args 是一個(gè)參數(shù)名稱列表,實(shí)際上這些參數(shù)并沒(méi)有多大用處外里。
例如:
from django.dispatch import Signal
pizza_done = Signal(providing_args=["toppings", "size"])
這段代碼聲明了 pizza_done 信號(hào)怎爵,它向接受者提供 toppings 和 size 參數(shù)。
Receiver 函數(shù)
receiver 相當(dāng)于一個(gè)回調(diào)函數(shù)盅蝗,相關(guān)事件發(fā)生了就會(huì)觸發(fā)它被執(zhí)行鳖链。
def my_callback(sender, **kwargs):
print "Request finished!"
注意:函數(shù)接受一個(gè) sender 參數(shù),以及通配符關(guān)鍵字參數(shù)(**kwargs)墩莫;所有信號(hào)處理器都必須接受這些參數(shù)芙委。
可是每次取值都得從 kwargs 中取,看起來(lái)可讀性也變差了狂秦。個(gè)人用法是函數(shù)中經(jīng)常會(huì)用到的參數(shù)直接列出來(lái)灌侣,比如:
def my_callback(sender, instance, created, **kwargs):
pass
接下來(lái),要做的就是綁定 receiver 到 signal裂问,有兩種方法可以將一個(gè)接收器連接到信號(hào)侧啼。
- 手動(dòng)連接:
from django.core.signals import request_finished
request_finished.connect(my_callback)
- 使用 receiver 裝飾器來(lái)自動(dòng)連接:
receiver(signal)
Parameters: signal – A signal or a list of signals to connect a function to.?
from django.core.signals import request_finished
from django.dispatch import receiver
@receiver(request_finished)
def my_callback(sender, **kwargs):
print "Request finished!"
現(xiàn)在玖姑,我們的 my_callback 函數(shù)會(huì)在每次請(qǐng)求結(jié)束時(shí)調(diào)用。
當(dāng)你這么設(shè)置好了后慨菱,就會(huì)收到各種 signals 發(fā)來(lái)的通知〈魉Γ可是符喝,如果我的回調(diào)函數(shù)只對(duì)特定 sender 感興趣的話怎么辦呢?可以通過(guò)指定 connect 函數(shù)的 sender 參數(shù)來(lái)過(guò)濾:
Signal.connect(receiver[, sender=None, weak=True, dispatch_uid=None])
Parameters:
receiver – 和這個(gè)信號(hào)連接的回調(diào)函數(shù)
sender – 指定一個(gè)特定的 sender甜孤,來(lái)從它那里接受信號(hào)
weak – Django 通常以弱引用儲(chǔ)存信號(hào)處理器协饲。這就是說(shuō),如果你的 receiver 是個(gè)局部變量缴川,可能會(huì)被垃圾回收茉稠。當(dāng)你調(diào)用信號(hào)的 connect()方法時(shí),傳遞 weak=False 來(lái)防止這樣做
dispatch_uid – 一個(gè)信號(hào) receiver 的唯一標(biāo)識(shí)符把夸,以防信號(hào)多次發(fā)送而线,通常是一個(gè)字符串?
使用裝飾器方式過(guò)濾:
from django.db.models.signals import pre_save
from django.dispatch import receiver
from myapp.models import MyModel
@receiver(pre_save, sender=MyModel)
def my_handler(sender, **kwargs):
...
my_handler 函數(shù)只在 MyModel 實(shí)例保存時(shí)被調(diào)用。
發(fā)送信號(hào)
Django 中有兩種方法用于發(fā)送信號(hào):
Signal.send(sender, **kwargs)
Signal.send_robust(sender, **kwargs)
調(diào)用 Signal.send() 或者 Signal.send_robust() 來(lái)發(fā)送信號(hào)恋日。你必須提供 sender 參數(shù)(大多數(shù)情況下它是一個(gè)類)膀篮,并且可以提供盡可能多的關(guān)鍵字參數(shù)。
例如岂膳,這樣來(lái)發(fā)送我們的 pizza_done 信號(hào):
class PizzaStore(object):
...
def send_pizza(self, toppings, size):
pizza_done.send(sender=self.__class__, toppings=toppings, size=size)
...
send() 和 send_robust() 都會(huì)返回一個(gè)含有二元組的列表 [(receiver, response), ...
]誓竿,它代表了被調(diào)用的接收器函數(shù)和他們的響應(yīng)值。
send() 與 send_robust() 在處理 receiver 函數(shù)產(chǎn)生的異常時(shí)有所不同谈截。send() 不會(huì)捕獲任何由 receiver 產(chǎn)生的異常筷屡。它會(huì)簡(jiǎn)單地讓錯(cuò)誤往上傳遞。所以在錯(cuò)誤產(chǎn)生的情況簸喂,不是所有 receiver 都會(huì)獲得通知毙死。
send_robust() 捕獲所有繼承自 Python Exception 類的異常,并且確保所有 receiver 都能得到信號(hào)的通知娘赴。如果發(fā)生錯(cuò)誤规哲,錯(cuò)誤實(shí)例會(huì)在產(chǎn)生錯(cuò)誤的 receiver 的二元組中返回。
斷開(kāi)信號(hào)
Signal.disconnect([receiver=None, sender=None, weak=True, dispatch_uid=None])
調(diào)用 Signal.disconnect() 來(lái)斷開(kāi)信號(hào)的接收器诽表。 Signal.connect() 中描述了所有參數(shù)唉锌。如果接收器成功斷開(kāi),返回 True 竿奏,否則返回 False袄简。
receiver 參數(shù)表示要斷開(kāi)的已注冊(cè) receiver。如果使用 dispatch_uid 標(biāo)識(shí) receiver泛啸,它可以為 None绿语。