在真實的企業生產環境中,咱們會遇到各類各樣的需求,好比對客戶端請求進行過濾,將知足某些條件的客戶端請求過濾掉,這時咱們能夠利用Django的中間件來實現該需求;或者,我但願每次model的save()方法被調用先後,都要寫一條日誌到日誌文件中,而此時咱們能夠經過Django提供的內置信號post_save來實現,本文介紹一下Django的高級功能——信號。html
「信號分發器」容許解耦的應用在框架的其它地方發生操做時會被通知到。 簡單來講,信號容許特定的sender通知一組receiver某些操做已經發生。 這在多處代碼和同一事件有關聯的狀況下頗有用。shell
Django2.1官文對signal的解釋以下: 數據庫
Django includes a "signal dispatcher" which helps allow decoupled applications get notified when actions occur elsewhere in the framework.
In a nutshell, signals allow certain senders to notify a set of receivers that some action has taken place.
They're especially useful when many pieces of code may be interested in the same events.
翻譯過來就是:Django框架內部包含了一個信號調度器,它的做用是能夠將框架內部發生的任何操做都通知到功能獨立的應用程序,固然,咱們也能夠縮小發送者和接收者的範圍,即指定具體的發送者和接受者,假設咱們的程序中有多個業務邏輯都在等待某一個事件發生以後再繼續執行後面的代碼,那麼此時,信號是很是有用的。django
Django內置的信號以下(加粗的是比較經常使用的):瀏覽器
Model signals pre_init # django的model執行其構造方法前,自動觸發 post_init # django的model執行其構造方法後,自動觸發 pre_save # django的model對象保存前,自動觸發 post_save # django的model對象保存後,自動觸發 pre_delete # django的model對象刪除前,自動觸發 post_delete # django的model對象刪除後,自動觸發 m2m_changed # django的model中使用m2m字段操做第三張表(add,remove,clear)先後,自動觸發 class_prepared # 程序啓動時,檢測已註冊的app中modal類,對於每個類,自動觸發 Management signals pre_migrate # 執行migrate命令前,自動觸發 post_migrate # 執行migrate命令後,自動觸發 Request/response signals request_started # 請求到來前,自動觸發 request_finished # 請求結束後,自動觸發 got_request_exception # 請求異常後,自動觸發 Test signals setting_changed # 使用test測試修改配置文件時,自動觸發 template_rendered # 使用test測試渲染模板時,自動觸發 Database Wrappers connection_created # 建立數據庫鏈接時,自動觸發
對於Django內置的信號,僅需註冊指定信號,當程序執行相應操做時,自動觸發註冊函數。註冊信號,寫入與project同名的文件夾下的_init_.py文件中,也是換數據庫引擎的地方。app
如上圖,項目名爲signalTest,咱們在與項目同名的包的__init__.py文件中註冊Django內置的信號,而後使用就能夠了。框架
方式一:直接監聽函數
def my_callback(sender, **kwargs): print("Request finished!")
from django.core.signals import request_finished request_finished.connect(my_callback)
方式二:使用裝飾器監聽post
def my_callback(sender, **kwargs): print("Request finished!") from django.core.signals import request_finished from django.dispatch import receiver @receiver(request_finished) def my_callback(sender, **kwargs): print("Request finished!")
from django.db.models.signals import pre_save from django.dispatch import receiver from myapp.models import MyModel # 在MyModel對應的數據保存前執行my_handler函數 @receiver(pre_save, sender=MyModel) def my_handler(sender, **kwargs): xxx
註冊完內置信號後,只要知足觸發條件,對應的函數會在相應的條件下去執行,不用人手動去調用。 測試
更多內置信號的說明請參考這篇博客:http://www.javashuo.com/article/p-engwidfj-bc.html
首先要知道的是,咱們用的信號都是django.dispatch.Signal這個類的實例。
在任意的py文件中定義信號:
import django.dispatch signal_done = django.dispatch.Signal(providing_args=["height", "width"])
def callback(sender, **kwargs): print("callback") print(sender,kwargs) signal_done.connect(callback)
from 路徑 import signal_done signal_done.send(sender='Naruto',height=123, width=456)
最後須要注意:因爲內置信號的觸發者已經集成到Django中,因此其會自動調用;而對於自定義信號則須要開發者在對應位置指定觸發。
上面介紹的一種方式是在項目同名的包的__init__.py文件中去註冊信號。
咱們也能夠利用Django在路由分發以前作一下信號的註冊操做。
這裏用到了Django啓動的機制:django.dispatch.Signal在django.setup()的過程當中,它會遍歷settings.INSTALLED_APPS列表中的每一項,並調用該AppConfig的ready方法,所以,將recevier訂閱signal的過程放置於ready方法中就能保證該代碼的執行。
咱們來拿一個具體的項目爲例。
1、項目的目錄結構以下
(1)把全部的信號都寫在了signals包中,而且signals包中的__init__.py文件中實例化Signal類的對象(注意Python在import一個包的時候會執行裏面的__init__文件),執行的操做我寫在了handlers.py文件中;
(2)而後,利用Django的啓動的原理,我把信號的註冊寫在了apps.py的SignalappConfig類的ready方法中,保證在路由分發以前就註冊自定義的信號。
2、註冊信號的具體寫法
signals/__init__.py:
# -*- coding:utf-8 -*- from django.dispatch import Signal my_signal = Signal(providing_args=[])
signals/handlers.py:
# -*- coding:utf-8 -*- def my_callback(sender,**kwargs): print('my_callback...')
signalapp/apps.py
from django.apps import AppConfig # 從外部導入自定義信號及處理的函數 from signals import my_signal from signals.handlers import my_callback class SignalappConfig(AppConfig): name = 'signalapp' def ready(self): # 註冊信號 my_signal.connect(my_callback)
3、使用自定義的信號
作一個簡單的路由與視圖測試一下這個自定義的信號是否成功:
signalTest/urls.py:
from django.contrib import admin from django.urls import path from signalapp import views urlpatterns = [ path('admin/', admin.site.urls), path('index/',views.index,name='index'), ]
sjgnalapp/views.py:
from django.shortcuts import render,HttpResponse # 導入自定義信號的處理函數 from signals.handlers import my_callback def index(request): # 使用自定義信號 my_callback(sender='index') return HttpResponse('OK')
啓動Django程序後咱們在瀏覽器中輸入127.0.0.1:8000/index,能夠看到在後臺打印出了自定義信號處理函數中所打印的數據:
my_callback...
默認狀況下,某些信號會被屢次發送,可是,一般,咱們只但願接收某個或者某些特定的發送者發出的信號,好比說django.db.models.signals.pre_saves,該信號,它在每一個model的save()方法被執行的時候被髮送,不過,不少狀況下,咱們只想記錄某個特定的model的save()方法被執行時的日誌。
在上述狀況下,咱們能夠指定只接受咱們指定的信號發送者發出的信號。
仍是用django.db.models.signals.pre_saves舉例,下面咱們來演示如何指定發送者
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): xxx
若是不但願再接收某個信號,咱們能夠調用Signal.disconnect()方法。