Django的信號機制解讀

Django的信號

Django的信號機制不同於Linux的信號機制,Django 中的信號用於在框架執行操作時解耦。當某些動作發生的時候,系統會根據信號定義的函數執行相應的操作

Django的信號主要包含以下三個要素:

  • 發送者(sender):信號的發出方。
  • 信號(signal):發送的信號本身。
  • 接收者(receiver):信號的接收者。

其中接受者就是回調函數,會把這個函數註冊到信號之上。當特定事件發生之後,發送者發送信號,然後執行回調函數。

Django信號的使用

查看Django Signal的源碼,看到Django的信號存在以下方法

除了上面的幾個方法,還有幾個屬性

  • lock
  • recievers
  • sender_receivers_cache
  • use_caching

根據第一趴介紹知道,要使用Django的信號,需要滿足三要素(發送者、接受者、和信號)

Django通過connect() 函數監聽 信號,接受發送者發送的信號,然後執行接受者(回調函數)

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

其中

  • recievers 是接受者,本質就是一個回調函數
  • sender 是發送信號的主體,如果connect連接的時候,sender 是None(默認也是None)代表該信號接受所有發送者發送的該信號;否則只接受具體的發送者(一個Python對象,比如Django的 Model對象)
  • weak -- Django 默認將信號處理程序存儲爲弱引用。因此,如果你的接收器是本地函數,則可能會對其進行垃圾回收。要防止這種情況發生,當你要調用 connect() 方法時請傳入 weak=False。
  • dispatch_uid 在可能發送重複信號的情況下,信號接收器的唯一標識符。

具體使用方式

1、需要定義一個回調函數

def my_callback(sender, **kwargs):
    print("Request finished!")

2、把回調函數 註冊到對應的信號

from django.core.signals import request_finished
request_finished.connect(my_callback)

注意這裏沒有指定sender,那麼就是接受任意發送者哦

或者更簡單的方式是使用 receiver 函數

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.core.signals import request_finished
from django.dispatch import receiver

@receiver(request_finished, sender="MyTagModel")
def my_callback(sender, **kwargs):
    print("Request finished!")

==> 劃重點

一般我們會把 回調函數信號註冊 放到一個應用的目錄下的 signals.py 文件中去

然後在該應用的 apps.py 中應用

from django.apps import AppConfig
from django.core.signals import request_finished

class MyAppConfig(AppConfig):
    ...

    def ready(self):
        # Implicitly connect signal handlers decorated with @receiver.
        from . import signals
        # Explicitly connect a signal handler.
        request_finished.connect(signals.my_callback)

這樣只要改應用安裝在 INSTALLED_APPS 中去,那麼Django就能識別到具體的信號(包括自定義的信號)以及進行信號的處理(因爲已經自動通過 connect進行監聽 )

爲啥會自動監聽呢,當然是 Django的 AppConfig 下的 ready() 函數的作用

自定義信號

首先有個核心點需要明確

所有的信號都是 django.dispatch.Signal 的實例。

比如這裏新增一個發送郵件的信號,每次新增Post之後,發送郵件給相關訂閱的人

# demoapp/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver

from .models import Post

@receiver(post_save, sender=Post)
def send_mail(sender, instance, created, **kwargs):
    if created:
        print(f"current instance {instance}")
        print("Try to send mail to subscriber")

然後把信號導入到 appConfig 的ready() 函數中去

# demoapp/apps.py

from django.apps import AppConfig


class DemoappConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'demoapp'

    def ready(self):
        import demoapp.signals

然後我們在admin註冊Post之後,新增Post,在命令行日誌中就能看到

current instance DemoPost First One
Try to send mail to subscriber
[02/Feb/2023 13:47:29] "POST /admin/demoapp/post/add/ HTTP/1.1" 302 0

還有另外一個 主動發送信號的方式

1、先定義回調函數 callback_func

2、定義一個信號 mail_send_signal = Signal()

3、回調函數註冊到信號 mail_send_signal.connect(callback_func)

3、主動發送信號 mail_send_signal.send(sender=xxx, **kwargs)

具體的代碼實現,可以手動嘗試哦,實踐出真知嘛~

擴展:查看Django信號的接受者

Django內置信號的reciever查看

(kfzops) [ 23-02-02 16:04 ] [ colinspace.com ] python manage.py shell
Python 3.9.6 (default, Jul 16 2021, 13:41:17)
[Clang 12.0.5 (clang-1205.0.22.11)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.db.models.signals import post_migrate
>>> post_migrate.receivers
[(('django.contrib.auth.management.create_permissions', 4304618416), <weakref at 0x10232b310; to 'function' at 0x10231d1f0 (create_permissions)>), ((4331786592, 4304618416), <weakref at 0x102581e00; to 'function' at 0x10231d160 (create_contenttypes)>)]

擴展:Django內置信號

Django內置了很多有用的信號,大概有以下幾類,可以作爲了解。

模型相關的信號

  • pre_init
  • post_init
  • pre_save
  • post_save
  • pre_delete
  • post_delete
  • m2m_changed
  • class_prepared

其中

1、pre_init/post_init 分別會在模型的__init__() 方法調用 之前/之後發出

2、pre_save/post_save 分別會在模型的save() 方法調用 之前/之後發出

3、pre_delete/post_delete 分別會在模型的delete() 方法調用 之前/之後發出

4、m2m_changed 嚴格意義上來說是由ManyToManyField 字段發生變化的時候發出的

5、class_prepared 比較特殊一般不建議用

django-admin 發出的信號,確切的說是 Django admin在執行 python manage.py migrate的時候發出的信號

  • pre_migrete
  • post_migrate

顧名思義,不做過多解釋

請求響應信號,也就是Django 發起request和響應response的信號

  • request_started
  • request_finished
  • get_request_exception

另外還有幾個特殊的信號

1、setting_changed 只有當運行測試用例的時候發出

2、template_render 當測試系統渲染模板的時候發出

3、connect_created 當數據庫連接啓動的時候,數據庫管理器發出

參考文檔

1、 https://docs.djangoproject.com/zh-hans/4.1/topics/signals/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章