Django: Signal

 20th August 2020 at 2:19pm

Django 的 signal 听起来很神奇,但是它并不是什么复杂的东西。它看起来像一个事件驱动系统,但是并不像 asyncio 那样有复杂的调度过程,而仅仅是同步地在某些操作发生时调用。

它的流程大概是这样:

  • 信号定义者:在框架代码中定义信号(使用 Signal 类),并在适当时机发送信号(signal.send())以触发信号处理函数
  • 信号使用者:一般在业务配置(AppConfig)中注册信号处理函数(signal.connect()),当信号被发送时,这些处理函数会被 Django 框架执行

举一个 Django auth 中的例子,无关代码被略去:

## 定义
# django.contrib.auth.signals 中定义了 user_logged_in signal
from django.dispatch import Signal
user_logged_in = Signal(providing_args=['request', 'user'])

# django.contrib.auth.login 处的代码,表明 login 最后被调用时,最后会触发信号
def login(request, user, backend=None):
    # ...
    user_logged_in.send(sender=user.__class__, request=request, user=user)

## 使用
# Django Auth 中本身也使用了这个信号,用于用户登录时更新其最后登录时间
# django.contrib.auth.apps.AuthConfig.ready 
class AuthConfig(AppConfig):
    def ready(self):
        from .models import update_last_login
        user_logged_in.connect(update_last_login, dispatch_uid='update_last_login')