Django框架7:信號

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

相關文章
相關標籤/搜索