Django进阶 ----信号机制

文章目录

1. 工作机制

Django 中的信号工作机制依赖三个主要要素:

  • 发送者(sender):信号的发出方,可以是模型,也可以是视图。当某个操作发生时,发送者会发出信号
  • 信号(signal):发送的信号本身。Django内置了许多信号,比如模型保存后发出的post_save信号。
  • 接收者(receiver):信号的接收者,其本质是一个简单的回调函数。将这个函数注册到信号上,当特定的事件发生时,发送者发送信号,回调函数就会被执行。

2. 应用场景

  • 信号主要用于Django项目内不同事件的联动,实现程序的解耦
  • 当模型A有变动时,模型B与模型C收到发出的信号后同步更新
  • 当一个数据表数据有所改变时,监听这个信号的函数可以及时清除已失效的缓存
  • 有人刚刚回复了你的贴子,可以通过信号进行推送
  • Django中信号监听函数不是异步执行,而是同步执行,所以需要异步执行耗时的任务时(比如发送邮件或写入文件),不建议使用Django自带的信号

3. 示例

  • 有一个Profile模型,与User模型是一对一的关系
  • 希望创建User对象实例时自动创建Profile对象实例
  • 但更新User对象实例时不创建新的Profile对象实例

可以自定义 create_user_profile 和 save_user_profile 两个监听函数,同时监听 sender (User模型) 发出的 post_save 信号。由于post_save可同时用于模型的创建和更新,我们用 if created 这个判断来加以区别

from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
 
class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    birth_date = models.DateField(null=True, blank=True)

# 监听User模型创建    
@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
   if created:
       Profile.objects.create(user=instance)

# 监听User模型更新  
@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()
  • 使用信号清除缓存
  • 当模型A被更新或被删除时,会分别发出 post_savepost_delete 的信号
  • 监听这两个信号的 receivers 函数会自动清除缓存里的A对象列表
from django.core.cache import cache
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver

@receiver(post_save, sender=ModelA)
def cache_post_save_handler(sender, **kwargs):
    cache.delete('cached_a_objects')
    
@receiver(post_delete, sender=ModelA)
def cache_post_delete_handler(sender, **kwargs):
     cache.delete('cached_a_objects')
  • 有时为了防止信号多次发送,可以通过 dispatch_uidreceiver 函数提供唯一标识符
@receiver(post_delete, sender=ModelA, dispatch_uid = "unique_identifier")

4. 常用内置信号

  • 监听 pre_save 和 post_save 信号的回调函数不能再调用 save() 方法,否则回出现死循环
  • Django的 update 方法不会发出 pre_save 和 post_save 的信号
信号 作用
pre_save 在模型调用 save() 方法之前发送
post_save 在模型调用 save() 方法之后发送
pre_init 在模型调用_init_() 方法之前发送
post_init 在模型调用 _init_() 方法之后发送
pre_delete 在模型调用 delete() 方法或查询集调用 delete() 方法之前发送
post_delete 在模型调用 delete() 方法或查询集调用 delete() 方法之后发送
request_started Django建立HTTP 请求时发送
request_finished Django关闭HTTP 请求时发送
m2m_changed 在模型多对多关系改变后发送

5. 信号监听函数

  • 把自定义的信号监听函数集中放在 app 对应文件夹下的 signals.py 文件里,便于后期集中维护
# app/signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import User, Profile

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
  if created:
      Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()


# app/apps.py
from django.apps import AppConfig
 
class AccountConfig(AppConfig):
    name = 'account'
 
    def ready(self):
        import account.signals
        
        
# app/__init__.py中增加如下代码:
default_app_config = 'account.apps.AccountConfig'

6. 自定义信号

1. 自定义信号

  • 每个自定义的信号,都是Signal类的实例
  • 在app目录下新建一个 signals.py 文件,创建一个名为 my_signal 的信号,它包含 msg 这个参数
  • 这个参数在信号触发的时候需要传递。当监听函数收到这个信号时,会得到msg参数的值
from django.dispatch import Signal

my_signal = Signal(providing_args=['msg'])

2. 触发信号

  • 视图中进行某个操作时可以使用 send 方法触发自定义的信号,并设定 msg 的值
from . import signals
# Create your views here.

def index(request):
    signals.my_signal.send(sender=None, msg='Hello world')
    return render(request, template_name='index.html')

3. 关联监听函数与信号

  • 当用户访问/index/视图时,Django都会发出my_signal的信号(包含msg这个参数)。回调函数收到这个信号后就会打印出msg的值
from django.dispatch import Signal, Receiver

my_signal = Signal(providing_args=['msg'])

@receiver(my_signal)
def my_signal_callback(sender, **kwargs):
    print(kwargs['msg']) # 打印Hello world!
上一篇:创建异形窗口[4]


下一篇:SysUtils.StrUpper、SysUtils.StrLower