1. 程式人生 > 其它 >Django signals 訊號總結

Django signals 訊號總結

技術標籤:Djangopythondjango

故事背景

最近在做的專案中都遇到使用 django 訊號量,故此總結訊號相關的使用。

什麼是訊號

Django 中內建一個 訊號排程器,它可以幫助應用程式在框架中的其他地方發生某些固定操作時能夠得到通知。簡而言之:訊號允許某些傳送器通知一組接收器某些操作已經發生。當接受器接受到這一通知時可以做響應的處理。

Django 中內建的訊號集

模型訊號

  • 模型初始化時

    • django.db.models.signals.pre_init(模型在初始化時發出)
    • django.db.models.signals.post_init(模型在被初始化之後發出)
  • 模型 save()方法相關訊號

    • django.db.models.signals.pre_save (模型在被儲存之前發出)
    • django.db.model.signals.post_save(模型物件被儲存之後發出)
  • 模型 delete()方法相關訊號

    • dajngo.db.models.signals.pre_delete(模型被刪除之前)
    • django.db.models.signals.post_delete(模型被刪除之後)
  • 模型多對多(manytomanyField)欄位發生更改時

    • django.db.models.signals.m2m_changed(模型中多對多欄位發生改變)
  • 執行遷移之前

    • django.db.models.signals.pre_migrate(在模型遷移之前執行)
    • django.db.models.signals.post_migrate(在模型遷移之後執行)

網路請求相關

  • django 發起或者結束一個 http 請求相關訊號
    • django.core.signals.request_started(django 發起一個 http 請求前)
    • django.core.signals.request_finished(django 發起一個 http 請求之後)
  • django 傳送請求發生錯誤時
    • django.core.signals.got_request_exception(傳送請求發生錯誤時)

Django 訊號量使用

使用 Signal.connect()方法註冊一個接收器函式,當傳送訊號時呼叫接收器。訊號的所有接收器函式都會按照註冊時的順序一個接一個呼叫。

Signal.connect(receiver, sender=None, weak=True, dispatch_uid=None)

  • receiver:將連線到此訊號的回撥函式
  • sender:指定要從其接收訊號的特定傳送方
  • weak:Django 預設將訊號處理程式儲存為弱引用,因此如果你的接收器函式時本地函式,則可能會對其進行垃圾回收。如果想避免這種情況發生可以當你要呼叫 connct() 方法時傳入 weak=False
  • disptch_uid:在可能傳送重複訊號的情況下,訊號接收器的唯一標識

接受訊號函式

下面我們定義一個接收函式,接受函式可以是任何 python 函式或者方法:

def my_callback(sender, **kwargs):
  print('hello world')

注意⚠️:該函式接受一個 sneder 引數以及關鍵字引數(**kwargs),所有訊號處理程式都必須接受這些引數。

連線接受函式

Django 提供兩種方法可以將接收器連線到訊號。

  • 方式一:Signal.connet(my_callback)

    from django.core.signals import request_finished
    
    request_finished.connect(my_callback)
    
  • 方式二:receiver(sigal)

    • 引數 signal:一個用於連線函式的訊號包含多個訊號列表
    • 接受函式和連線接受函式放在 在一起
    from django.core.signals import request_finished
    from django.dispatch import receiver
    
    @receiver(request_finished)
    def my_callback(sender, **kwargs):
      print('hello world')
    

    以上兩種方式都能到達 my_callback 函式在每次請求完成時被呼叫

接受到特定傳送器傳送的訊號

在某些情況下有些訊號被髮送多次,但你只對這些訊號中某部分訊號感興趣。假設我們有 User 以及 Enterprise 兩個 model 我們只想接受當模型 User 儲存後的訊號,程式碼如下:

from django.db.models.signals import post_save
from django.dispatch import receiver
from . models import User

@receiver(post_save, sender=Udser)
# 這裡的 instance 可以表示為當前變化的模型例項
def my_handel(sender, instance, **kwargs):
  # to do ...

防止訊號重複

在某些情況下,接受器可能同時接受相同訊號多次,這可能會導致接收函式被註冊多次,因此對於一個訊號時間呼叫同樣多次,為防止這種相同訊號被接受多次的情況發生,我們可以傳遞一個唯一識別符號作為 dispatch_uid 引數來標識接收方函式。這個識別符號通常是一個字串,對於每個唯一 dispatch_uid 值,接受器函式值與訊號繫結一次:

from django.core.signals import post_save

post_save.connect(my_callback, dispatch_uid='my_unique_identifier')

django 訊號量實際使用

監聽某個模型中某個欄位的變化

假設我們有一個 User 模型,當我們使用者名稱發生改變時需要重置 token,這時我們只需要監聽 User 中 token 是否變化,如果變化則重置 token,使用訊號來實現這一需求

# 使用者模型

class User(models.Model):
	id = models.BigIntegerField(primary_key=True)
  name = models.CharField(max_length=32, verbose_name='使用者名稱')
  areaCode = models.CharField(max_length=10, blank=True, verbose_name='手機區號')
  phone = models.CharField(max_length=20, verbose_name='使用者手機號', blank=True)
  email = models.EmailField(max_length=50, verbose_name='使用者郵箱', blank=True)
  avatar = models.CharField(max_length=255, verbose_name='使用者頭像')
  token = models.CharField(max_length=50, verbose_name='token')
# 訊號量實現以上場景

from django.db.models.signals import post_init, post_save
from django.dispatch import receiver

from . models import User


@receiver(post_init, sender=User)
def init_signal(sender, instance, **kwargs):
  # 在模型初始化時將 name 賦值給 __origin_name
  instance.__origin_name = instance.name
  
  
@receiver(post_save, sender=User)
def save_signal(sender, instance, **kwargs):
  # 當模型被儲存時檢視此時的 name 和 __origin_name 是否相等,如果不相等則重置 token
  if instance.__origin_name and instance.__origin_name != instance.name:
    # 重置 token