Django中的信号

791 阅读4分钟

Django中的信号

一篇讲signal讲的比较好的文章:www.atemon.com/blog/django…

Django中提供了一个“信号分发器”,允许被接耦合的应用在其他地方发生操作时会被通知到,在一个事件影响多处代码的情况下很有用。

你可以将多个方法绑定到发送的信号上,使Django在一次数据变动时,处理更多的业务逻辑。信号系统允许一个或多个发送者,将信号发送给一组接收者。

信号系统包含三个部分:

  1. 发送者
  2. 信号
  3. 接收者

Django中内置了几个信号接收器,可以直接注册然后添加相应的操作

pre_init  # 实例化一个数据库模型前
post_init  # 实例化一个数据库模型后

pre_save  # 保存数据前
post_save  # 保存数据后

pre_delete  # 删除数据前
post_delete  # 删除数据后

m2m_changed  # 多对多关系改变后

pre_migrate  # 迁移前
post_migrate  # 迁移后
# 更多的方法在后面详细讲解

## 接收器的简单使用

首先应该初始化你的APP配置,在APP的apps.py中,配置你的APP

from django.apps import AppConfig


class TestAppConfig(AppConfig):
    name = 'test_app'

    def ready(self):
        # 启动应用后导入自定义的signal回调方法
        import test_app.signals

然后在__init__.py中注册你的配置

# 绑定app config
default_app_config = 'test_app.apps.TestAppConfig'

如果使接收器生效需要将自定义的方法,连接到接收器上。有两种方式,使用connect()方法,或者使用装饰器receiver()

使用connect方法

语法为:

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

参数:

  • receiver :当前信号连接的回调函数,也就是处理信号的函数。
  • sender :指定从哪个发送方接收信号。
  • weak : 是否弱引用
  • dispatch_uid :信号接收器的唯一标识符,以防信号多次发送。

关于weak参数的理解,可以参考:

weak – Django stores signal handlers as weak references by default. Thus, if your receiver is a local function, it may be garbage collected. To prevent this, pass weak=False when you call the signal’s connect() method.

from djangp.db.models.signals import post_save
from app.models import AppModel


def save_callback(sender, instance, **kwargs):
    """
    对象保存后的回调方法
    """
    print(sender)
    print(instance)
    print(kwargs)
    print('ORM has saved the object and now in callback method')


# sender是一个model,当model对象保存到数据库中时,自动调用这个回调函数
post_save.connect(save_callback, sender=AppModel)

使用register装饰器

receiver()装饰器接受的参数和connect()方法相同

使用receiver来注册信号接受函数:

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


@receiver(request_finished)
def finished_callback(sender, **kwargs):
    print(sender)
    print(kwargs)
    print('request finished and now in callback method')

具体使用哪种方式看个人习惯。

防止信号重复

为了防止信号重复,可以设置dispatch_uid参数来标识你的接收器,标识符通常是一个字符串,如下所示:

from django.core.signals import request_finished

request_finished.connect(finished_callback, dispatch_uid='first_callback_id')

发送信号

在试图或其他的业务逻辑里面,向信号接收器发送信号:

save_callback.sende(sender='some function or class', args1='args1', arg2='arg2')

在选择发送信号的方式有两种一种使用Signal.send,还有一种是Signal.send_robut。

send()send_robust()处理接收器功能引起的异常的方式不同。

send()并不能捕获由接收器提出的任何异常; 它只是允许错误传播。因此,在面对错误时,不是所有接收器都可以被通知信号。

send_robust()捕获从Python Exception类派生的所有错误,并确保所有接收器都收到信号通知。如果发生错误,则会在引发错误的接收器的元组对中返回错误实例。

断开信号

也可以将接收器的信号断开:

from django.dispatch import Signal
Signal.disconnect(receiver=None, sender=None, dispatch_uid=None)

Django内置的信号接收器

Django内置多个信号接收器,可以直接将自己的业务逻辑直接注册到信号接受器上,接受的参数如下:

关于modles的信号接收器

from django.db.models import signals

signals.pre_init(sender, *args, **kwargs)
signals.post_init(sender, instance)
signals.pre_save(sender, instance, raw, using, update_fields)
signals.post_save(sender, instance, raw, using, update_fields)
signals.pre_delete(sender, instance, using)
signals.post_delete(sender, instance, using)

m2m_changed比较特殊,是ManyToManyField发送的,实现了pre_save/post_savepre_delete/post_delete

参数:

  • sender:描述ManyToManyField的中间模型类,这个中间模型类会在一个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

关于请求的信号接收器

from django.core import signals

signals.request_started(sender, *args, **kwargs)
signals.request_finished(sender, *args, **kwargs)
signals.got_request_exception(sender, *args, **kwargs)