如何创建Django信号

2017-08-24

django signals (图片: https://unsplash.com/photos/NuE8Nu3otjo)

Django Signals是一种允许解耦应用程序在某些事件发生时得到通知的策略。假设您希望每次更新给定的模型实例时使缓存的页面无效,但代码库中有几个可以更新该模型的地方。您可以使用信号来执行此操作,并在每次触发特定模型的保存方法时挂起要执行的一些代码段。

另一个常见的用例是当您通过一对一关系使用“配置文件”策略来扩展自定义Django用户时。我们通常使用“信号调度程序”来监听用户的post_save事件,同时也更新配置文件实例。我在另一篇文章中介绍了这种情况,您可以在这里阅读: 如何扩展Django用户模型

在本教程中,我将向您介绍内置信号,并提供有关最佳实践的一般建议。


何时应用?

在我们前进之前,知道你应该使用它:

  • 当许多代码段对同一事件感兴趣时;
  • 当您需要与解耦应用程序进行交互时,例如:
    • Django核心模型;
    • 由第三方应用定义的模型。

怎么运行的?

如果您熟悉"观察者设计模式",那么Django就会实现它。或者至少是为了相同的目的。

信号机械中有两个关键要素:发送者和接收者。顾名思义,发送者是负责发送信号的 发送者,接收者是接收到该信号的人,然后做某事。

甲接收机必须是一个函数或要接收的信号的实例方法。

甲发件人必须是一个Python对象,或无接收来自发送者的任何事件。

发送者和接收者之间的连接是通过“信号调度员”完成的,信号调度员是通过连接方式的信号实例 。

Django内核还定义了一个ModelSignal,它是Signal的一个子类,它允许发件人被懒惰地指定为一个app_label.ModelName表单的字符串。但是,一般来说,您将始终想要使用 Signal类来创建自定义信号。

所以要接收一个信号,你需要注册一个接收器功能,当使用Signal.connect()方法发送信号时,该功能被调用。


用法

我们来看看post_save内置的信号。其代码存在于django.db.models.signals模块中。在模型完成执行其保存方法后,该特定信号就会立即触发。

from django.contrib.auth.models import User
from django.db.models.signals import post_save

def save_profile(sender, instance, **kwargs):
    instance.profile.save()

post_save.connect(save_profile, sender=User)

在上面的例子中,save_profile是我们的接收机功能,User是发送者,post_save是 信号。您可以将其读取为:每当User实例完成其save方法的执行时,该 save_profile函数将被执行。

如果你像这样对发件人的参数进行抑制post_save.connect(save_profile),那么这个save_profile函数将在任何Django模型执行save方法后执行。

注册信号的另一种方法是使用@receiver装饰器:

def receiver(signal, **kwargs)

的信号参数可以是信号实例或列表/元组信号的实例。

见下面的例子:

from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

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

如果要将接收机功能注册到几个信号,可以这样做:

@receiver([post_save, post_delete], sender=User)

代码在哪里?

由于导入代码,可能会发生一些副作用。所以,根据您注册应用程序信号的位置。 这是一个好主意,以避免把它里面的模型模块或应用程序根模块。

Django文档建议将信号放在您的应用配置文件中。通常将signals.py放在Django应用程序例如: “profiles”:

profiles/signals.py:

from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

from cmdbox.profiles.models import 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()

profiles/app.py:

from django.apps import AppConfig
from django.utils.translation import ugettext_lazy as _

class ProfilesConfig(AppConfig):
    name = 'cmdbox.profiles'
    verbose_name = _('profiles')

    def ready(self):
        import cmdbox.profiles.signals  # noqa

profiles/init.py:

default_app_config = 'cmdbox.profiles.apps.ProfilesConfig'

在上面的例子中,因为我正在使用装饰器, 只需在 ready() 方法中的导入信号模块,。如果使用 connect() 方法连接接收器功能,请参考下面的示例:@receiver()

profiles/signals.py:

from cmdbox.profiles.models import Profile

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

def save_user_profile(sender, instance, **kwargs):
    instance.profile.save()

profiles/app.py:

from django.apps import AppConfig
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.utils.translation import ugettext_lazy as _

from cmdbox.profiles.signals import create_user_profile, save_user_profile

class ProfilesConfig(AppConfig):
    name = 'cmdbox.profiles'
    verbose_name = _('profiles')

    def ready(self):
        post_save.connect(create_user_profile, sender=User)
        post_save.connect(save_user_profile, sender=User)

profiles/init.py:

default_app_config = 'cmdbox.profiles.apps.ProfilesConfig'

注:

如果您已经在 INSTALLED_APPS 设置中引用了AppConfig, 则不需要配置 profiles/init.py


Django 内建 Signals

在这里,您可以找到一些有用的内置信号的列表。这是不完整的,但可能是最常见的。

models 信号

django.db.models.signals.pre_init:

receiver_function(sender, *args, **kwargs)

django.db.models.signals.post_init:

receiver_function(sender, instance)

django.db.models.signals.pre_save:

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

django.db.models.signals.post_save:

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

django.db.models.signals.pre_delete:

receiver_function(sender, instance, using)

django.db.models.signals.post_delete:

receiver_function(sender, instance, using)

django.db.models.signals.m2m_changed:

receiver_function(sender, instance, action, reverse, model, pk_set, using)

Request/Response Signals

django.core.signals.request_started:

receiver_function(sender, environ)

django.core.signals.request_finished:

receiver_function(sender, environ)

django.core.signals.got_request_exception:

receiver_function(sender, request)

如果您想查看整个列表,请点击此处访问官方文档。

https://simpleisbetterthancomplex.com/tutorial/2016/07/28/how-to-create-django-signals.html