1. 程式人生 > >Django中訊號的實現

Django中訊號的實現

signal在django中用於傳送通知,在django中內建的訊號包括 post_save, pre_delete 等;使用這些訊號可以很方便實現業務邏輯與model之間的解耦。
內建signal的使用
django 內部有些定義好的signal供我們使用:

模型相關:

pre_save 物件save前觸發

post_save 物件save後觸發

pre_delete 物件delete前觸發

post_delete 物件delete後觸發

m2m_changed ManyToManyField 欄位更新後觸發

請求相關:

request_started 一個request請求前觸發

request_finished request請求後觸發

應用場景

適用於:當修改model的時候,需要執行一些與當前app無關額外的操作,比如邏輯中包含第三方的庫;執行的多個行為且後續仍可能新增操作等;
不適用:修改當前app中的某個model,這種場景建議放到save函式中執行;

適用場景,比如當用戶訂單支付成功之後,通常需要做以下事情

傳送郵件通知使用者支付成功
通知聊天程式(阿里旺旺)
通知賣家備貨
開始追蹤物流資訊
其他可能的行為…

以上行為都依賴於支付成功事件;如果上述行為寫到model的save() 函式中,就會使得業務邏輯緊耦合;推薦的做法是使用signal傳送一個通知到訊息佇列,其他的任務(程序)監聽訊息佇列,然後分別執行上述行為;
signal應用的例子:

order/models.py

class Order(models.Model):
    """

    """
    ORDER_STATUS = (
        ("TRADE_CLOSED", "超時關閉"),
        ("PAYING", "待支付"),
        ("UNCONFIRMED", "待付款確認"),
        ("CONFIRMED", "已確認"),
    )

    user = models.ForeignKey(UserModel, related_name="user_orders", verbose_name="使用者", db_index=True)
    # 訂單號規則,使用統一方法建立,主機id,程序號,時間ms+隨機數
    order_sn = models.CharField(max_length=35, verbose_name="訂單號", null=True, blank=True, unique=True, db_index=True)
    trade_no = models.CharField(max_length=100, unique=True, null=True, blank=True, verbose_name=u"交易號", db_index=True)

    pay_status = models.CharField(choices=ORDER_STATUS, default="PAYING", blank=True, max_length=30, verbose_name="訂單狀態")
    post_message = models.CharField(max_length=200, verbose_name="訂單留言", default="", blank=True, null=True)
    order_mount = models.FloatField(default=0.0, verbose_name="訂單金額")
    
    confirm_time = models.DateTimeField(null=True, blank=True, verbose_name="確認時間", default=None)
    create_time = models.DateTimeField(auto_now_add=True, verbose_name="建立時間")

    class Meta:
        verbose_name = u"訂單"
        verbose_name_plural = verbose_name

order/signals.py

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

@receiver(post_save, sender=Order)
def order_change(sender, instance=None, created=False, **kwargs):
    if not created and instance:
        if instance.pay_status == "CONFIRMED":
            # 傳送訂單資訊到訊息佇列

apps.py

#這裡需要import signals檔案
from django.apps import AppConfig
from django.db.models.signals import post_save

class OrderConfig(AppConfig):
    def ready(self):
        import order.signals

可能遇到的問題
1、我們定義在signals.py檔案中的所有註冊事件都未執行
2、在signal中的部分註冊事件未執行
原因:
1、所有事件未執行–檢查是否在apps.py中import signals
2、部分註冊時間未執行-檢查是否註冊函式名重複

在這裡插入圖片描述
1、apps.py 檔案內import signals,執行向post_save 註冊回撥函式receiver
2、models執行save()時候,呼叫post_save的send()
3、send函式遍歷所有receiver並執行回撥函式(receiver 裝飾的自定義函式),因此我們的邏輯就會被執行;