Django Signals 信号机制详解:内置信号、监听与自定义

白榆
白榆
发布时间:2026-10-13 | 3人浏览过
原创

本文详细解析Django中的Signals信号机制,涵盖Model signals与Request/response signals两大类别。重点讲解pre_init、post_save、m2m_changed等内置信号的触发时机与参数,演示如何通过connect方法和装饰器注册信号接收器,并介绍dispatch_uid防重机制及自定义信号的完整实现流程。

Django Model 内置信号详解

1、Model signals

django.db.models.signals 模块提供了作用于 Django Model 操作的一系列信号,用于在模型生命周期的关键节点执行自定义逻辑。

1)pre_init()

django.db.models.signals.pre_init

该信号在模型实例化时触发,具体执行时机位于 __init__() 方法之前。

它包含三个参数:

pre_init(sender, args, kwargs)

sender:指代创建该实例的模型类。

args:传递给构造函数的位置参数列表。

kwargs:通过字典形式传递的关键字参数。

2)post_init()

django.db.models.signals.post_init

作为 pre_init 的对应信号,post_init 同样作用于模型实例化过程,但它在 __init__() 方法执行完毕后触发。

它接收两个参数:

post_init(sender, instance)

sender:与 pre_init 相同,指创建实例的模型类。

instance:当前创建的模型实例对象。

3)pre_save()

django.db.models.signals.pre_save

在调用 Model 的 save() 方法保存数据之前触发。

该信号包含 5 个参数:

pre_save(sender, instance, raw, using, update_fields)

sender:对应的 Model 类。

instance:即将被保存的模型实例。

raw:布尔值,若模型是通过 loaddata 等方式全部保存则为 True。

using:指定使用的数据库别名。

update_fields:传递的待更新字段集合,若未指定则值为 None。

4)post_save()

djang.db.models.post_save

在 Model 的 save() 方法执行完毕后触发。

该信号包含 6 个参数:

post_save(sender, instance, created, raw, using, update_fields)

sender:Model 类。

instance:被保存的 Model 实例。

created:布尔值,若此次操作创建了新记录则为 True。

raw:布尔值,若模型被全部保存(如 loaddata)则为 True。

using:使用的数据库别名。

update_fields:传递的待更新字段集合,未传递时为 None。

5)pre_delete()

django.db.models.signals.pre_delete

在执行 Model 的 delete() 方法或 QuerySet 的 delete() 方法之前触发。

pre_delete(sender, instance, using)

sender:Model 类。

instance:被删除的模型实例。

using:使用的数据库别名。

6)post_delete()

django.db.models.signals.post_delete

在执行 Model 的 delete() 方法或 QuerySet 的 delete() 方法之后触发。

post_delete(sender, instance, using)

sender:Model 类。

instance:被删除的实例。需注意,此时该实例已从数据库中删除,记录不再存在,使用此实例时需格外小心。

using:使用的数据库别名。

7)m2m_changed()

django.db.models.signals.m2m_changed

当 Model 的 ManyToManyField 关系发生改变时发送。严格来说,这不是模型信号,而是由 ManyToManyField 发送,但因实现了 pre_save/post_save 和 pre_delete/post_delete 的逻辑,故归类于 Model signals 中。

该信号包含多个参数:

sender:描述 ManyToManyField 的中间模型类,Django 在定义 many-to-many 字段时自动创建,可通过 many-to-many 字段的 through 属性访问。

instance:被更新的多对多关系实例,可以是 sender 本身,也可以是 ManyToManyField 的关系类。

action:指明作用于关系更新类型的字符串,包括以下几种情况:

"pre_add"/"post_add":在向关系添加一个或多个对象前/后发送。

"pre_remove"/"post_remove":从关系中删除一个或多个对象前/后发送。

"pre_clear"/"post_clear":在关系清空之前/之后发送。

reverse:布尔值,表示正在修改的是正向关系(False)还是反向关系(True)。

model:被添加、删除或清空的对象所属的类。

pk_set:对于 add/remove 操作,pk_set 是一个包含从关系中添加或删除的对象主键的集合;对于 clear 操作,pk_set 为 None。

举例说明:

假设有两个实例,且存在如下关系:


class Topping(models.Model):

pass

class Pizza(models.Model):


toppings = ManyToManyFields(Topping)

我们像这样连接一个处理器:


from django.db.models.signals import m2m_changed

def toppings_changed(sender, **kwargs):

pass

m2m_changed.connect(toppings_changed, sender=Pizza.toppings.through)

然后我们对上面的类执行如下操作:


p = Pizza.objects.create(...)

t = Topping.objects.create(...)

p.toppings.add(t)

此时,对应的上述参数分别如下:

sender:描述 ManyToManyField 的中间类,即 Pizza.toppings.through。

instance:被更新的多对多关系实例,即 P(本例中,Pizza 对应被更改)。

action:先是 "pre_add",然后执行上面的 add() 操作,最后再调用 "post_add"。

reverse:本例中,Pizza 包含 ManyToManyField topping,调用 P.toppings.add(),属于正向更新,故 reverse 为 False。

model:被添加、删除或清空的类,本例中 Topping 被添加到 Pizza。

pk_set:{t.id}。

若我们执行以下操作:

t.pizza_set.remove(p)

此时对应的参数为:

sender:同上。

instance:t(本例中,Topping 实例被更改)。

action:先是 "pre_remove",然后执行 remove 操作,再执行 "post_remove"。

reverse:True,本例中属于反向操作。

model:p。

pk_set:{p.id}。

8)class_prepared

django.db.models.signals.class_prepared

当模型类准备好时发送,即模型被创建并注册到 Django 的模型系统中时触发。

该信号通常由 Django 内部使用,一般不会被第三方应用直接调用。

Request/Response 信号机制

2、Request/response signals

此类信号在处理 HTTP 请求的生命周期中发出。

1)request_started()

django.core.signals.request_started

在 Django 开始处理 HTTP 请求时发送。

request_started(sender, environ)

2)request_finished()

django.core.signals.request_finished

在 Django 处理完 HTTP 请求后发送。

3)got_request_exception()

django.core.signals.got_request_exception

在处理 HTTP 请求过程中遇到错误时发送。

Django 信号监听与注册方法

3、使用信号

1)监听信号

若要接收信号,可使用 Signals.connect() 方法注册一个接收器函数,当信号发送时该函数将被调用。

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

receiver:将连接到此信号的回调函数。

sender:指定要接收信号的特定发送方(可选)。

weak:Django 默认将信号处理程序存储为弱引用。若接收器是弱引用,可能被垃圾回收机制回收。为防止此情况,

我们在调用信号的 connect() 方法时,传递 weak=False。

dispatch_uid:为信号接收方定义的唯一标识符,用于防止重复信号发送导致的重复注册。

接下来以 HTTP 请求中的 request_finished 信号为例:

2)定义接收函数

def my_func_callback(sender, **kwargs):

print("request_finished")

如上所示,所有的接收函数必须包含 sender 和关键字参数(**kwargs)两个参数。

3)连接接收函数

有两种方法将接收器和信号连接起来,我们可以选择手动连接,如下:

from django.core.signals import request_finished

request_finished.connect(my_func_callback)

我们还可以选择通过装饰器来连接信号和接收器:


from django.dispatch import receiver

from django.core.signals import request_finished

@receiver(request_finished)

def my_func_callback(sender, **kwargs):

pass

注意:在实践中,信号处理程序通常定义在与它们相关的应用程序的 signals 子模块中,信号接收器连接在我们的应用程序配置类的 ready() 方法中。如果使用装饰器方式,我们只需要在 ready() 中导入 signals 子模块即可。

值得一提的是,在测试过程中,我们的 ready() 函数可能不止一次被执行,因此我们要保护我们的信号不要被重复注册。

4)连接到特定发送者发送的信号

在很多情况下,信号会被多次发送,但实际我们只对这些信号的某个子集感兴趣,例如前面提到的 pre_save() 信号。

此时,我们可以注册只接收特定发送者(即特定 Model)发送的信号。如下,我们可以指定我们需要接收的某个模型发送的信号:


from djang.db.models.signals import pre_save

from django.dispatch import receiver

from .model import MyModel

@receiver(pre_save, sender=MyModel)

def my_receiver(sender, **kwargs):

pass

这样,我们的 my_receiver() 函数将只有在 MyModel 被保存时被调用。

5)防止重复的信号:

在某些情况下,连接接收器到信号的代码可能会运行多次,这可能导致接收器函数注册不止一次,从而对单个信号事件调用多次。

例如使用信号在保存模型时发送电子邮件,可传递唯一标识符作为 dispatch_uid 参数,以识别接收函数。这个标识符通常是一个字符串。

最终结果是,对于每个唯一的信号,我们的接收器函数将只绑定到该信号一次。

from django.core.signals import request_finished

request_finished.connect(my_receiver, dispatch_uid="my_unique_identifier")

如我们注册时保存密码需要用到 post_save,新建 my_signals.py,在文件中加入下面代码:


from django.db.models.signals import post_save

from django.dispatch import receiver

from django.contrib.auth import get_user_model

user = get_user_model()

@receiver(signal=post_save, sender=user)

def create_user(sender, instance=None, created=False, **kwarg):

password = instance.password

instance.set_password(password)

instance.save()

然后在项目 apps 中重写 ready,将我们新建的 my_signals 引入即可。

Django 自定义信号实现

3、自定义信号

1)定义信号:

在项目根目录新建文件 self_signal.py。

import django.dispatch

my_signal = django.dispatch.Signals(providing_args=["aaa","bbb"])

2)注册信号(即信号接收器)

在项目应用下的 __init__.py 文件中注册。


from self_signal import my_signal

def register_my_signal(sender, **kwargs):

print("my signal msg:", sender, **kwargs)

my_signal.connect(register_my_signal)

3)触发信号

在 views 视图中编写如下代码:

from self_signal import my_signal

my_signal.send(sender="Python", aaa=111, bbb=2)

以上这篇基于 Django signals 信号作用及用法详解就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。

相关标签:

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表