歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Django Signals 從實踐到源碼分析

Django Signals 從實踐到源碼分析

日期:2017/3/1 9:26:25   编辑:Linux編程

當某個事件發生的時候,signal(信號)允許senders(發送者)用來通知receivers(接收者),通知receivers干嘛?你想要recivers干嘛就可以干嘛。這在多處代碼對同一個事件感興趣的時候就有用武之地了。 比如:Django提供了一個built-in signal,叫django.core.signals.request_finished,這個signal會在一個HTTP請求完成後發送。下面就用一個簡單的實例說明:在每個請求完成後打印"request finished"

編寫receiver

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,比如:

  1. django.db.models.signals.presave & django.db.models.signals.postsave

    Sent before or after a model’s save() method is called.

  2. 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.

  3. 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 的下載地址:請點這裡

Copyright © Linux教程網 All Rights Reserved