django包含了一個“信號分配器”使得當一些動作在框架的其他地方發生的時候,解耦的應用可以得到提醒。通俗來講,就是一些動作發生的時候,信號允許特定的發送者去提醒一些接受者,這是特別有用的設計因為有些代碼對某些事件是特別感興趣的,比如刪除動作。
為此,django提供了很多內置的信號,比如一些常用的功能(以幾個在django.db.models.signal目錄下的信號為例):
- save:pre_save和post_save
- delete:pre_delete和post_delete
- change:m2m_changed
如果你想了解更多,可以查閱django的內置信號文檔,本節的最后也會有一個所有內置信號的簡略介紹。
監聽信號
要想接受信號,你首先要注冊一個接收器函數,當信號被Signal.connect()方法發射的時候,這個函數會被調用
Signal.connect(receiver[,sender=None,weak=True,dispatch_uid=None])
參數解釋:
- receiver:連接到這個信號的回調函數
- sender:信號的發送者
- weak:是否是弱引用,默認是真。因此,如果你的接收器是是一個本地函數,會被當做垃圾回收,如果你不想,請在使用connect()方法的時候使用weak=False
- dispatch_uid:一個唯一的標識符給信號接收器,避免重復的信號被發送
下面讓我們來看一個具體的例子來解釋這些參數吧吧,這個例子以request_finished信號(每個HTTP請求結束的時候會被調用):
回調函數receiver
回調函數可以是一個函數或者方法,比如我們可以這樣定義一個接收器:
def my_callback(sender, **kwargs): print "Request finished!"
注意的是所有的信號處理器都需要這兩個參數:sender和**kwargs。因為所有的信號都是發送關鍵字參數的,可能你處理的時候沒有任何參數,但不意味着在處理的過程中(在你寫的處理函數之前)有任何的參數生成,如果沒有傳**kwargs參數的話,可能會發生問題;基於這樣的考慮,這兩個參數都是必須的。
連接到你的receiver回調函數
有兩種方法可以把信號和接收器連接到一起:
connect方法
from django.core.signals import request_finished request_finished.connect(my_callback)
裝飾器方法
from django.core.signals import request_finished from django.dispatch import receiver @receiver(request_finished) def my_callback(sender, **kwargs): print "Request finished!"
這樣配置之后,每次HTTP接受的時候都會調用這個接收器回調函數了
綁定特定的發送者
記上面之后,你會不會想到這樣的一個問題:每次都調用,會不會很煩啊?如果是我的話,我肯定覺得很煩,畢竟我不是想接受所有人的信號的,所以你需要設置sender關鍵字參數
第二個知識:每一類的信號都對應着特定的發送者,所以要綁定發送者也得綁定對應的發送者類型,例如,request_finished對應的是handler class,而pre_save對應則是model class
下面我們以pre-save為例子綁定特定的發送者(模型):
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):
預防重復的信號
使用dispatch_uid關鍵字參數
request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")
說完了如何監聽一個信號,下面我們繼續講解定義和發送信號吧】
定義信號
class Signal([providing_args=list])
所有的信號都是django.dispatch.Signal的實例,參數providing_args是一個信號提供給監聽器的參數名的列表,比如:
import django.dispatch pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])
這段代碼定義了一個pizza_done的信號,參數有toppings和size
發送信號
有兩個方法發送信號
Signal.send(sender,**kwargs)
Signal.send_robust(sender,**kwargs)
sender參數是必須的,關鍵字參數可選
class PizzaStore(object): ... def send_pizza(self, toppings, size): pizza_done.send(sender=self, toppings=toppings, size=size)
這兩種方法都返回一個元組對[(receiver,respose),...]的列表,一個代表被調用的receiver回調函數和他們的response的列表
這兩種方法的區別在於send不會捕捉任何的異常,(放任錯誤的傳播),而send_robust則是捕捉所有的異常,並確保每個接收器都知道這個信號(發生錯誤了)(如果發生錯誤的話,錯誤實體和發生錯誤的接收器作為一個元組對一起返回給那個列表
斷開信號
Signal.disconnect([receiver=None,sender=None,weak=True,dispatch_uid=None)
和監聽信號類似
receiver參數用來指明那個接收器被斷開,如果使用了dispatch_uid的話,receiver可以為None
總結,你可以使用django自帶的信號,也可以自定義自己的信號,信號可以connect,可以send也可以disconnect等等