django中的信号机制
1.1 什么是信号机制#
Django 框架包含了一个信号机制,它允许若干个发送者(sender)通知一组接收者(receiver)某些特定操作或事件(events)已经发生了, 接收者收到指令信号(signals)后再去执行特定的操作。
1.2 信号的工作机制#
Django 中的信号工作机制依赖如下三个主要要素:
信号的发出方,可以是模型,也可以是视图。当某个操作发生时,发送者会发出信号。
发送的信号本身。Django内置了许多信号,比如模型保存后发出的post_save信号。
信号的接收者,其本质是一个简单的回调函数。将这个函数注册到信号上,当特定的事件发生时,发送者发送信号,回调函数就会被执行。
1.3 信号的应用场景#
信号主要用于Django项目内不同事件的联动,实现程序的解耦。比如当模型A有变动时,模型B与模型C收到发出的信号后同步更新。又或当一个数据表数据有所改变时,监听这个信号的函数可以及时清除已失效的缓存。另外通知也是一个信号常用的场景,比如有人刚刚回复了你的贴子,可以通过信号进行推送。
注意:Django中信号监听函数不是异步执行,而是同步执行,所以需要异步执行耗时的任务时(比如发送邮件或写入文件),不建议使用Django自带的信号。
1.4 简单例子#
1.4.1#
'''
假如我们有一个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)
@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()
1.4.2#
'''
我们再来看一个使用信号清除缓存的例子。当模型A被更新或被删除时,会分别发出post_save和post_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')
@receiver(post_delete, sender=ModelA, dispatch_uid = "unique_identifier")
Django常用内置信号
pre_save & post_save: 在模型调用 save()方法之前或之后发送。
pre_init& post_init: 在模型调用_init_方法之前或之后发送。
pre_delete & post_delete: 在模型调用delete()方法或查询集调用delete() 方法之前或之后发送。
m2m_changed: 在模型多对多关系改变后发送。
request_started & request_finished: Django建立或关闭HTTP 请求时发送。
'''
注意:监听pre_save和post_save信号的回调函数不能再调用save()方法,否则回出现死循环。另外Django的update方法不会发出pre_save和post_save的信号。
'''
如何放置信号监听函数代码#
'''
在之前案例中,我们将Django信号的监听函数写在了models.py文件里。当一个app的与信号相关的自定义监听函数很多时,此时models.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()
from django.apps import AppConfig
class AccountConfig(AppConfig):
name = 'account'
def ready(self):
import account.signals
default_app_config = 'account.apps.AccountConfig'
自定义信号#
'''
每个自定义的信号,都是Signal类的实例。这里我们首先在app目录下新建一个signals.py文件,创建一个名为my_signal的信号,它包含有msg这个参数,这个参数在信号触发的时候需要传递。当监听函数收到这个信号时,会得到msg参数的值。
'''
from django.dispatch import Signal
my_signal = Signal(providing_args=['msg'])
'''
视图中进行某个操作时可以使用send方法触发自定义的信号,并设定msg的值。
'''
from . import signals
def index(request):
signals.my_signal.send(sender=None, msg='Hello world')
return render(request, template_name='index.html')
'''
这样每当用户访问/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'])
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)