當某個事件發生的時候,signal(信號)允許senders(發送者)用來通知receivers(接收者),通知receivers干嘛?你想要recivers干嘛就可以干嘛。這在多處代碼對同一個事件感興趣的時候就有用武之地了。 比如:Django提供了一個built-in signal,叫django.core.signals.request_finished
,這個signal會在一個HTTP請求完成後發送。下面就用一個簡單的實例說明:在每個請求完成後打印"request finished"
reciver是一個普通的callable對象,簡單來說就是一個可被調用的函數,但是需要注意的是它需要接收一個參數sender
和一個關鍵字參數**kwargs
def my_callback(sender, **kwargs):
'''
這是個receiver函數
你可以在這裡做愛做的的事情
'''
print sender
print kwargs
print("Request finished!")
這裡我們先撇開sender和kwargs後面再分析,reciver函數寫好之後,就需要把request_finished
信號連接(注冊)到my_callback
。
from django.core.signals import request_finished
request_finished.connect(my_callback)
現在請求一個URL路徑/hello
,後台打印的結果:
[31/Mar/2014 21:52:33] "GET /hello/ HTTP/1.1" 200 263
<class 'django.core.handlers.wsgi.WSGIHandler'>
{'signal': <django.dispatch.dispatcher.Signal object at 0x0262E510>}
Request finished!
以上就是一個signal的執行流程,那麼django內部是怎麼實現的呢?為什麼調用了reciver.connect後,my_callback就能得到執行了呢?且看源代碼分析:
request_finished定義在文件django.core.signals.py裡面:
from django.dispatch import Signal
request_started = Signal()
request_finished = Signal()
got_request_exception = Signal(providing_args=["request"])
request_finished
就是Signal的實例。GET請求完成後會執行my_callback
方法,為什麼這麼神奇,我們順著request_finished的思路來猜想,既然是請求完成了,那麼此時response對象也生成了,那麼神奇的事情一定是在response裡面發生的。去response.py文件裡面看看:django.http.response.py
def close(self):
for closable in self._closable_objects:
try:
closable.close()
except Exception:
pass
signals.request_finished.send(sender=self._handler_class)
看到在response的close方法裡面有send方法,而且這個sender就是我們在前面看到的django.core.handlers.wsgi.WSGIHandler'
,這個send方法會發送信號給所有的receivers。
#Signal.send方法的源代碼:
responses = []
if not self.receivers or self.sender_receivers_cache.get(sender) is NO_RECEIVERS:
return responses
for receiver in self._live_receivers(sender):
response = receiver(signal=self, sender=sender, **named)
responses.append((receiver, response))
return responses
注意:你可以看到在for循環裡面迭代的調用的receiver方法。以上就是django內部的執行原理。思考下send方式是signal的而不是sender的呢?從面向對象的角度來說,誰是對象的擁有者,誰就提供相應的方法。比如汽車的drive方法肯定是由汽車提供而不是由人。
我們需要做的只是編寫receiver,然後調用signal.connect方法,相當於把receiver注冊到signal上去。當事件觸發時,相應的signal就會通知所有注冊的receivers得到調用。尼瑪,這是傳說中的觀察者模式。
連接receiver函數還有另外一個方法,用裝飾器:
@receiver(request_finished):
def my_handler(sender, **kwages):
'''
django還提供了很多內置的signals,比如:
django.db.models.signals.presave & django.db.models.signals.postsave
Sent before or after a model’s save() method is called.
django.db.models.signals.predelete & django.db.models.signals.postdelete
Sent before or after a model’s delete() method or queryset’s delete() method is called.
django.db.models.signals.m2m_changed
Sent when a ManyToManyField on a model is changed.
signal還可以指定具體的senders,比如pre_save這個signal是在Model對象保存在被發送,但是我希望只有某一類Model保存的時候才發送,你就可以指定:
@receiver(pre_save, MyModel):
def my_handle(sender, **kwargs):
pass
這樣每次只有保存MyModel實例後才會發送,其他的XXModel就會忽略掉。
完!
Django1.8返回json字符串和接收post的json字符串內容 http://www.linuxidc.com/Linux/2015-07/120226.htm
如何使用 Docker 組件開發 Django 項目? http://www.linuxidc.com/Linux/2015-07/119961.htm
Ubuntu Server 12.04 安裝Nginx+uWSGI+Django環境 http://www.linuxidc.com/Linux/2012-05/60639.htm
Django+Nginx+uWSGI 部署 http://www.linuxidc.com/Linux/2013-02/79862.htm
Django實戰教程 http://www.linuxidc.com/Linux/2013-09/90277.htm
Django Python MySQL Linux 開發環境搭建 http://www.linuxidc.com/Linux/2013-09/90638.htm
Django 的詳細介紹:請點這裡
Django 的下載地址:請點這裡