[ Django的内置信号 ]


目录:

一,模型信号

1. pre_init

pre_init
django.db.models.signals.pre_init
每当你实例化一个 Django 模型时,这个信号都会在模型的 __init__() 方法的开头发出。

用此信号发送的参数:

sender
刚刚创建了一个实例的模型类。
args
传递给 __init__() 的位置参数列表。
kwargs
传递给 __init__() 的关键字参数字典。

2. post_init

post_init
django.db.models.signals.post_init
和 pre_init 一样,但这个是在 __init__() 方法完成后发送的。

用此信号发送的参数:

sender
如上:刚刚创建了一个实例的模型类。
instance
刚刚创建的模型的实际实例。

3. pre_save

pre_save
django.db.models.signals.pre_save
这是在模型的 save() 方法开始时发送的。

用此信号发送的参数:

sender
模型类
instance
实际被保存的实例。
raw
一个布尔值;True 如果模型完全按照显示的方式保存(即加载固定数据时)。不应该查询/修改数据库中的其他记录,因为数据库可能还没有达到一致的状态。
using
正在使用的数据库别名。
update_fields
传递给 Model.save() 的要更新的字段集,如果 update_fields 没有传递给 save(),则为 None。

4. post_save

post_save
django.db.models.signals.post_save
就像 pre_save 一样,但在 save() 方法的最后发送。

用此信号发送的参数:

sender
模型类
instance
实际被保存的实例。
created
一个布尔值;True 如果创建了一个新记录。
raw
一个布尔值;True 如果模型完全按照显示的方式保存(即加载固定数据时)。不应该查询/修改数据库中的其他记录,因为数据库可能还没有达到一致的状态。
using
正在使用的数据库别名。
update_fields
传递给 Model.save() 的要更新的字段集,如果 update_fields 没有传递给 save(),则为 None。

5. pre_delete

pre_delete
django.db.models.signals.pre_delete
在模型的 delete() 方法和查询集的 delete() 方法开始时发送。

用此信号发送的参数:

sender
模型类
instance
实际被删除的实例。
using
正在使用的数据库别名。

6. post_delete

post_delete
django.db.models.signals.post_delete
就像 pre_delete 一样,但在模型的 delete() 方法和查询集的 delete() 方法结束时发送。

用此信号发送的参数:

sender
模型类
instance
实际被删除的实例。

请注意,该对象将不再在数据库中,所以要非常小心地处理这个实例。

using
正在使用的数据库别名。

7. m2m_changed

m2m_changed
django.db.models.signals.m2m_changed
当一个模型实例上的 ManyToManyField 被改变时发出。严格来说,这不是一个模型信号,因为它是由 ManyToManyField 发送的,但由于它是对 pre_save/post_save 和 pre_delete/post_delete 的补充,当涉及到跟踪模型的变化时,它被包含在这里。

用此信号发送的参数:

sender
中间模型类描述 ManyToManyField。当定义了多对多字段时,这个类会自动创建;你可以使用多对多字段上的 through 属性来访问它。
instance
多对多关系被更新的实例。这可以是 sender 的实例,或者是 ManyToManyField 所关联的类的实例。
action
表示对关系进行更新的类型的字符串。可以是以下类型之一:

"pre_add"
在一个或多个对象被添加到关系 之前 发送。
"post_add"
在一个或多个对象被添加到关系 之后 发送。
"pre_remove"
在一个或多个对象从关系中删除 之前 发送。
"post_remove"
在一个或多个对象从关系中删除 之后 发送。
"pre_clear"
在关系被清除 之前 发送。
"post_clear"
在关系被清除 之后 发送。
reverse
表示关系的哪一面被更新(即被修改的是正向关系还是反向关系)。
model
从关系中添加、删除或清除的对象的类别。
pk_set
对于 pre_add 和 post_add 动作,这是一组将被或已经被添加到关系中的主键值。这可能是提交添加的值的一个子集,因为插入必须过滤现有的值,以避免数据库 IntegrityError。

对于 pre_remove 和 post_remove 动作来说,这是一组被提交从关系中删除的主键值。这并不取决于这些值是否会被实际删除或已经被删除。特别是,不存在的值可能会被提交,并且会出现在 pk_set 中,即使它们对数据库没有影响。

对于 pre_clear 和 post_clear 动作,是 None。

using
正在使用的数据库别名。
例如,如果一个 Pizza 可以有多个 Topping 对象,模型如下:
class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)

如果我们连接一个这样的处理程序:

from django.db.models.signals import m2m_changed

def toppings_changed(sender, **kwargs):
    # Do something
    pass

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

然后做了这样的事情:

>>> p = Pizza.objects.create(...)
>>> t = Topping.objects.create(...)
>>> p.toppings.add(t)

发送给 m2m_changed 处理程序的参数(在上面的例子中,toppings_changed)将是:

参数  值
sender  Pizza.toppings.through (中间的 m2m 类)
instance    p (被修改的 Pizza 实例)
action  "pre_add" (后面是一个单独的信号 "post_add")
reverse False (Pizza 包含 ManyToManyField,所以这个调用修改了前向关系)
model   Topping (添加到 Pizza 中的对象类别)
pk_set  {t.id} (因为只有 Topping t 被添加到关系中)
using   "default" (因为默认的路由器在这里发送写入)
And if we would then do something like this:
>>> t.pizza_set.remove(p)

8. class_prepared

class_prepared
django.db.models.signals.class_prepared
Sent whenever a model class has been "prepared" -- that is, once a model has been defined and registered with Django's model system. Django uses this signal internally; it's not generally used in third-party applications.

由于这个信号是在应用注册表填充过程中发出的,而 AppConfig.ready() 是在应用注册表完全填充后运行的,所以不能在该方法中连接接收器。一种可能是用 AppConfig.__init__() 来代替连接它们,注意不要导入模型或触发对应用注册表的调用。

用此信号发送的参数:

sender
刚刚准备好的模型类。

二.管理信号

1. pre_migrate

pre_migrate
django.db.models.signals.pre_migrate
由 migrate 命令在开始安装应用程序之前发出。对于缺乏 models 模块的应用程序,它不会发出。

用此信号发送的参数:

sender
一个 AppConfig 实例,用于即将迁移/同步的应用程序。
app_config
同 sender。
verbosity
Indicates how much information manage.py is printing on screen. See the --verbosity flag for details.

监听 pre_migrate 的函数应该根据这个参数的值来调整它们向屏幕输出的内容。

interactive
如果 interactive 为 True,则可以安全地提示用户在命令行上输入东西。如果 interactive 为 False,则监听该信号的函数不应试图提示任何东西。

例如 django.contrib.auth 应用只有在 interactive 为 True 时才会提示创建超级用户。

stdout
A stream-like object where verbose output should be redirected.
using
命令将运行的数据库的别名。
plan
将用于迁移运行的迁移计划。虽然计划不是公开的 API,但这允许在极少数情况下有必要知道计划。计划是一个由二元元组组成的列表,第一项是迁移类的实例,第二项显示迁移是否被回滚(True)或应用(False)。
apps
Apps 的实例,包含迁移运行前的项目状态。它应该代替全局 apps 注册表来检索你要执行操作的模型。

2. post_migrate

post_migrate
django.db.models.signals.post_migrate
在 migrate (即使没有运行迁移)和 flush 命令结束时发出。对于缺乏 models 模块的应用程序,它不会被发出。

该信号的处理者不能进行数据库模式的改变,因为如果在 migrate 命令期间运行 flush 命令,可能会导致 flush 命令失败。

用此信号发送的参数:

sender
一个 AppConfig 实例,用于刚刚安装的应用程序。
app_config
同 sender。
verbosity
Indicates how much information manage.py is printing on screen. See the --verbosity flag for details.

监听 post_migrate 的函数应该根据这个参数的值来调整它们向屏幕输出的内容。

interactive
如果 interactive 为 True,则可以安全地提示用户在命令行上输入东西。如果 interactive 为 False,则监听该信号的函数不应试图提示任何东西。

例如 django.contrib.auth 应用只有在 interactive 为 True 时才会提示创建超级用户。

stdout
A stream-like object where verbose output should be redirected.
using
用于同步的数据库别名。默认为 default 数据库。
plan
迁移运行时使用的迁移计划。虽然计划不是公开的 API,但这允许在极少数情况下有必要知道计划。计划是一个由二元元组组成的列表,第一项是迁移类的实例,第二项显示迁移是否被回滚(True)或应用(False)。
apps
Apps 的一个实例,包含迁移运行后项目的状态。它应该代替全局的 apps 注册表来检索你要执行操作的模型。
例如,你可以在一个 AppConfig 中注册一个回调,像这样:
from django.apps import AppConfig
from django.db.models.signals import post_migrate


def my_callback(sender, **kwargs):
    # Your specific logic here
    pass


class MyAppConfig(AppConfig):
    ...

    def ready(self):
        post_migrate.connect(my_callback, sender=self)

三. 请求/响应信号

1. request_started

request_started
django.core.signals.request_started
当 Django 开始处理一个 HTTP 请求时发送。

用此信号发送的参数:

sender
处理程序类 —— 例如 django.core.handlers.wsgi.wsgiHandler —— 处理该请求。
environ
向请求提供的 environ 字典。

2. request_finished

request_finished
django.core.signals.request_finished
当 Django 完成向客户端发送 HTTP 响应时发送。

用此信号发送的参数:

sender
处理程序类,同上。

3. got_request_exception

got_request_exception
django.core.signals.got_request_exception
当 Django 在处理一个传入的 HTTP 请求时遇到异常时,就会发出这个信号。

用此信号发送的参数:

sender
未使用(总是 None)。
request
HttpRequest 对象。

四. 测试信号

1. setting_changed

测试信号
只有当 运行测试 时才会发出信号。

setting_changed
django.test.signals.setting_changed
当通过 django.test.TestCase.settings() 上下文管理器或 django.test.override_settings() 装饰器/上下文管理器改变配置值时,会发出这个信号。

它实际上被发送了两次:当应用新的值时("setup")和当恢复原始值时("drawdown")。使用 enter 参数来区分这两种情况。

你也可以从 django.core.signals 导入这个信号,以避免在非测试情况下从 django.test 导入。

用此信号发送的参数:

sender
配置处理程序。
setting
配置的名称。
value
更改后的配置值。对于最初不存在的配置,在 "teardown" 阶段,value 是 None。
enter
一个布尔值;True 如果配置被应用,False 如果恢复。

2. template_rendered

template_rendered
django.test.signals.template_rendered
当测试系统渲染一个模板时发出。这个信号在 Django 服务器正常运行时不会发出,只有在测试时才会发出。

用此信号发送的参数:

sender
被渲染的 Template 对象。
template
与发送器相同
context
渲染模板的 Context。

五.数据库包装器

数据库包装器
当数据库连接启动时,数据库包装器发出的信号。

connection_created
django.db.backends.signals.connection_created
当数据库包装器与数据库进行初始连接时发送。 如果你想向 SQL 后端发送任何连接后的命令,这一点特别有用。

用此信号发送的参数:

sender
数据库封装类 —— 即 django.db.backends.postgresql.DatabaseWrapper 或 django.db.backends.mysql.DatabaseWrapper 等。
connection
打开的数据库连接。这在多数据库配置中可以用来区分不同数据库的连接信号。

官方文档案例

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


def my_callback(sender, **kwargs):
    print("Setting changed!")


class MyAppConfig(AppConfig):
    ...

    def ready(self):
        setting_changed.connect(my_callback)

或者使用装饰器

from django.core.signals import request_finished
from django.dispatch import receiver


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

可以选择添加nid

from django.core.signals import request_finished

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

在app中注册信号文件

signals.py

from django.dispatch import receiver
from django.db.models.signals import post_save
from .models import TaskPublish
from common.celery_server import CeleryAgent
@receiver(post_save, sender=TaskPublish)
def task_close(sender, **kwargs):
    instance: TaskPublish = kwargs["instance"]
    if instance.status == TaskPublish.Status.DONE:
        task_id = instance.task_id
        print(f'关闭定时任务{task_id}')
        CeleryAgent.cancel(pk=task_id)

apps.py

from django.apps import AppConfig


class CentreConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'centre'

    verbose_name = '派单管理'

    def ready(self):
        __import__('centre.signals')