Django信號機制相關解釋與示例

Django 信號#

django自帶一套信號機制來幫助我們在框架的不同位置之間傳遞信息。也就是說,當某一事件發生時,信號系統可以允許一個或多個發送者(senders)將通知或信號(signals)發送給一組接受者(receivers)。

也就是觀察者模式,又叫發佈-訂閱(Publish/Subscribe)。當發生一些動作的時候,發出信號,然後監聽了這個信號的函數就會執行。

信號系統包含以下三要素:

  • 發送者-信號的發出方
  • 信號-信號本身
  • 接收者-信號的接受者

Django內置的信號

  1. Model signals
    1. django.db.models.signals.pre_save 在某個Model保存之前調用
    2. django.db.models.signals.post_save 在某個Model保存之後調用
    3. django.db.models.signals.pre_delete 在某個Model刪除之前調用
    4. django.db.models.signals.post_delete 在某個Model刪除之後調用
    5. django.db.models.signals.pre_init 在某個Model實例化__init__()方法執行前調用
    6. django.db.models.signals.post_init 在某個Model實例化__init__()方法執行後調用
    7. django.core.signals.m2m_changed 在某個Model實例上更改了ManyToManyField時發送
  2. Management signals
    1. django.core.signals.pre_migrate 在某個app更新(migrate)前發送
    2. django.core.signals.post_migrate 在某個app更新(migrate)後發送
  3. Request/response signals
    1. django.core.signals.request_started 在建立Http請求時發送
    2. django.core.signals.request_finished 在關閉Http請求時發送
    3. django.core.signals.got_request_exception 在請求異常時發送
  4. Test signals
    1. setting_changed 使用test測試修改配置文件時,自動觸發
    2. template_rendered 使用test測試渲染模板時,自動觸發
  5. Database Wrappers
    1. connection_created 創建數據庫連接時,自動觸發

對於Django內置的信號,僅需註冊指定信號,當程序執行相應操作時,自動觸發註冊函數

Copy
def my_callback(sender, **kwargs):
    print("Request finished!")

# 方法一:
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!")

監聽信號#

要接收信號,請使用Signal.connect()方法註冊一個接收器。當信號發送後,會調用這個接收器。

方法原型:

Copy
Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None)[source]

參數:

Copy
receiver :當前信號連接的回調函數,也就是處理信號的函數。 
sender :指定從哪個發送方接收信號。 
weak : 是否弱引用
dispatch_uid :信號接收器的唯一標識符,以防信號多次發送。

下面以如何接收每次HTTP請求結束後發送的信號爲例,連接到Django內置的現成的request_finished信號。

1. 編寫接收器#

接收器其實就是一個Python函數或者方法:

Copy
def my_callback(sender, **kwargs):
    print("Request finished!")

請注意,所有的接收器都必須接收一個sender參數和一個**kwargs通配符參數。

2. 連接接收器#

有兩種方法可以連接接收器,一種是下面的手動方式:

Copy
from django.core.signals import request_finished

request_finished.connect(my_callback)

另一種是使用receiver()裝飾器:

Copy
from django.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished)
def my_callback(sender, **kwargs):
    print("Request finished!")

3. 接收特定發送者的信號#

一個信號接收器,通常不需要接收所有的信號,只需要接收特定發送者發來的信號,所以需要在sender參數中,指定發送方。下面的例子,只接收MyModel模型的實例保存前的信號。

Copy
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):
    pass

4. 防止重複信號#

爲了防止重複信號,可以設置dispatch_uid參數來標識你的接收器,標識符通常是一個字符串,如下所示:

Copy
from django.core.signals import request_finished

request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")

最後的結果是,對於每個唯一的dispatch_uid值,你的接收器都只綁定到信號一次。

自定義信號#

除了Django爲我們提供的內置信號(比如前面列舉的那些),很多時候,我們需要自己定義信號。

類原型:class Signal(providing_args=list)[source]

所有的信號都是django.dispatch.Signal的實例。providing_args參數是一個列表,由信號將提供給監聽者的參數的名稱組成。可以在任何時候修改providing_args參數列表。

下面定義了一個新信號:

Copy
import django.dispatch

pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

上面的例子定義了pizza_done信號,它向接受者提供size和toppings 參數。

發送信號#

Django中有兩種方法用於發送信號。

Copy
Signal.send(sender, **kwargs)[source]


Signal.send_robust(sender,** kwargs)[source] 

必須提供sender參數(大部分情況下是一個類名),並且可以提供任意數量的其他關鍵字參數。

例如,這樣來發送前面的pizza_done信號:

Copy
import django.dispatch

pizza_done = django.dispatch.Signal(providing_args=["toppings", "size"])

class PizzaStore(object):
    ...

    def send_pizza(self, toppings, size):
        pizza_done.send(sender=self.__class__, toppings=toppings, size=size)
        ...

send()send_robust()返回一個元組對的列表[(receiver, response), ... ],表示接收器和響應值二元元組的列表。

斷開信號#

方法:

Copy
Signal.disconnect(receiver=None, sender=None, dispatch_uid=None)[source]

Signal.disconnect()用來斷開信號的接收器。和Signal.connect()中的參數相同。如果接收器成功斷開,返回True,否則返回False。

信號使用實例#

信號可能不太好理解,下面我在Django內編寫一個例子示範一下:

urls.py

Copy
from django.conf.urls import url
from django.contrib import admin
from signal_demo import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^signal/', views.signal_view),
]

views.py

Copy
from django.dispatch import receiver, Signal
from django.shortcuts import HttpResponse
import time

# 自定義信號
my_signal = Signal(providing_args=['path', 'time'])


def signal_view(request):
    # 接受到請求,發送信號
    res = my_signal.send(signal_view, path=request.path, time=time.strftime("%Y-%m-%d %H:%M:%S"))
    # 返回一個元組對的列表`[(receiver, response), ... ],表示接收器和響應值二元元組的列表
    print(res)
    return HttpResponse('200,ok')


@receiver(my_signal, sender=signal_view)
def my_callback(sender, **kwargs):
    print("我在%s時間收到來自%s的信號,請求url爲%s" % (kwargs['time'], sender, kwargs["path"]))
    return 'ok'

啓動Django服務

訪問 http://127.0.0.1:8000/signal/

console打印

Copy
[XX] "GET /signal/ HTTP/1.1" 200 6
我在XXX時間收到來自<function signal_view at 0x000001FA31DFCD08>的信號,請求url爲/signal/
[(<function my_callback at 0x000001FA31E20378>, 'ok')]

Django 信號原理,源碼分析#

觀察者模式,又叫發佈-訂閱(Publish/Subscribe)。當發生一些動作的時候,發出信號,然後監聽了這個信號的函數就會執行。

DJango實現信號的原理是: 回調函數,串行執行

  1. 發送者 發送 信號
  2. 信號 處理,回調 接收者
  3. 接收者處理
  4. 信號拿到接收者的返回值
  5. 發送者拿到信號的返回值(就是接收者的返回值)

參考#

  1. 官方文檔
  2. Django的信號機制
  3. 信號 signal
  4. Django中的信號
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章