1、Django信號定義:html
Django包含「信號調度器」,能夠在Django框架運行函數時,發送信號給指定的接收函數,使指定的接收函數作相應的操做。python
信號發生器是高耦合的程序典範。數據庫
2、Django的內置信號種類:django
Django官網關於信號https://docs.djangoproject.com/en/3.0/ref/signals/後端
一、Models信號app
from django.db.models.signals import pre_init pre_init # 實例化Model以前(在Models的__init__以前),發送信號 # sender:建立的模型類名 # 傳遞到__init__的列表參數 # 傳弟到__init__的字典參數 post_init(sender, instance) # 實例化Models時,在Models的__init__完成以後,發送信號 # serder: 建立的模型名 # instance: 模型的實例 pre_save # 在Models的save方法前,發送信號 # sender: 建立的模型名 # instance: 模型的實例 # raw:A boolean; True if the model is saved exactly as presented (i.e. when loading a fixture). One should not query/modify other records in the database as the database might not be in a consistent state yet. # using:正在使用的數據庫別名 # update_fields: 指定model.save()更新的字段,若是不指定,默認爲None post_save # 在Models的save方法以後。 # sender:模型類名 # instance: 一個被保存的確切的實例 # created: A boolean; True if a new record was created. # raw: 同pre_save # using: 同pre_save # update_fields: 同pre_save pre_delete # 的Models和Queryset的delete方法開始以前 # sender: 建立的模型名 # instance: 一個被刪除的確切的實例 # using: 正在使用的數據庫的別名 post_delete # Models和Queryset的delete方法以後 # sender: 模型的類名 # instance: 一個確切的實例 ,#請注意,對象將再也不在數據庫中,所以要很是當心地處理這個實例。 # using: 數據庫別名 m2m_changed # 在更改ManyToManyField時發送信號。action指定了跟蹤的變動方法,看起來和上面幾個不太同樣,它不是嚴格意義的信號 class_prepared() # 模型類已經「準備」時發送——也就是說,在模型已經被Django的模型系統定義和註冊以後。 https://docs.djangoproject.com/en/3.0/ref/signals/
二、管理類信號框架
from django.db.models.signals import pre_migrate per_migrate # 在migrate命令以前發送信號 post_migrate # 在migrate和flush命令結束後發送信號
三、請求/響應信號ide
from django.core.signals import request_started request_started # 處理客戶端請求前 request_finished # 向客戶端發送響應後 got_request_exception # 處理客戶端請求的過程當中,出現錯誤時,發送信號
四、測試信號函數
form django.test.signals import setting_changed setting_changed # 當經過django.test.TestCase.settings()上下文管理器或django.test.override_settings()裝飾器/上下文管理器更改設置的值時,將發送此信號。它實際上發送了兩次:應用新值時(「setup」)和恢復原始值時(「teardown」)。使用enter參數來區分這二者。 template_rendered # 當渲染模板時,只在測試系統起做用
五、數據庫包裝post
from django.db.backends.signals import connection_created connection_created # 啓動數據庫鏈接時數據庫包裝器發送的信號。若是您想將任何post鏈接命令發送到SQL後端,這一點特別有用。
3、監聽信號
要接收一個信號,使用signal .connect()方法註冊一個接收器函數。當信號發送時調用receiver函數。按照函數註冊的順序。接收函數一次調用一個。
Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None) # receiver: 接收信號的函數 # sender: 信號發送者 # weak: 信號接收函數與信號發送者是弱關聯,若是接收函數是本地函數常常被看成垃圾回收,設置爲weak=False更改 # dispatch_uid: 惟一標識,若是同一個 # 方法一:使用connect註冊 from django.core.signals import requst_finished def my_callback(sender,*args, **kwgrgs): # 定義一個接收信號的函數 print("request finished!") request_finished.connect(my_callback) # 註冊接收信號的函數 # 方法二:使用裝飾器 # 一、 from django.core.signals import request_finished from django.dispach import receiver @receiver(request_finished) # 使用裝飾器,每次請求完成後my_callback執行一次 def my_callback(sender,*args, **kwgrgs): # 定義一個接收信號的函數 print("request finished!") # 二、指定裝飾某個肯定的發送者 from django.db.models.signals import pre_save # 導入pre_save信號 from django.dispatch import receiver # 導入裝飾器 from myapp.models import MyModel # 導入DB模型 @receiver(pre_save, sender=MyModel) # 只有MyModel表保存的時候才發送信號給my_handler def my_handler(sender, **kwargs): print('pre save!') # 如何防止重複的信號 # 在某些狀況下,鏈接接收器和信號的代碼可能會運行屢次。這可能致使您的接收器函數被屢次註冊,從而對單個信號事件調用屢次。 # 若是此行爲存在問題(例如在模型保存時使用信號發送電子郵件),則傳遞唯一標識符做爲dispatch_uid參數,以標識接收方函數。這個標識符一般是一個字符串,儘管任何hashable對象都足夠了。最終結果是,對於每一個惟一的dispatch_uid值,你的接收器函數將只被綁定到信號一次: from django.core.signals import request_finished request_finished.connect(my_callback, dispatch_uid="my_unique_identifier")
4、自定義信號
爲何要用自定義信號?
隱式調用函數的信號,不方便調式。若是發送和接收信號都在一個項目中,最好使用顯式函數調用,方便調試。
# ------一、聲明一個信號:-------- from django.dispatch import Signal # 導入信號對象 pizza_done = Signal(providing_args=["toppings", "size"]) # 聲明pizza_done信號,並使用providing_args參數,指定了接收者toppings和size # 注意:providing_args列表能夠在任何須要的時候更改,因此沒有必要首次就嘗試獲取一個正確的API # -----二、發送信號------- # 在Django中有兩種發送信號的方式 # Signal.send(sender, **kwargs)[source] # 不捕獲錯誤,接收器在遇到錯誤時,有可能不會收到信號 # Signal.send_robust(sender, **kwargs)[source] # 捕獲錯誤,並確保全部接收器都接收信號 # sender參數:一般是類 # **kwargs : 和聲明信號時,參數providing_args相同? class PizzaStore: ... def send_pizza(self, toppings, size): pizza_done.send(sender=self.__class__, toppings=toppings, size=size) # pizza_done使用send方法發送 ... #-----三、信號和接收器斷開----- # Signal.disconnect(receiver=None, sender=None, dispatch_uid=None) # 參數與Signal.connect()相同 # 若是接收器斷開,返回值是True,不然,返回值是False # 接收器參數指示已註冊的接收器斷開鏈接。若是使用dispatch_uid來標識接收器,則它多是"無"。
5、實例:
一、使用自帶的pre_request信號
# 在任意位置新建一個新的pyton文件,這裏我在myapp下新建一個custom_signal.py文件,用戶來定義信號 from django.core.signals import request_start # 導入自帶的request_start信號 def re_callback(sender, **kwargs): # 定義Callback函數 print('request start!') request_start.connect(re_callback) # 註冊信號 # 使用信號,在要使用信號的APP的__init__.py文件下,導入註冊的信號文件,自動觸發信號 form myapp import custom_signal
二、自定義信號的使用
# 仍是在myapp下的custom_signal.py編輯 from django.signals import Signal pizza_done = Signal.dispatch(provid_args=['toppings', 'size']) def my_callback(sender, **kwargs) print('pizza done!') print(kwargs['toppings']) print(kwargs['size']) pozza_done.connect(my_callback) # 調用:函數在哪調用,就在哪裏導入。例:我在訪問index時讓發送信號 # views.py from myapp import custom_signal from django.shortcuts import render def index(request): from myapp import custom_signal custom_signal.pizza_done.send(sender='zen meban', toppings='tttt', size='ssss') return render(request, 'index.html') # 結果:當客戶端訪問index時,後端顯示 callback test! tttt ssss
https://docs.djangoproject.com/en/3.0/topics/signals/#receiver-functions