Django中的信号机制 (尽量避免使用)


Signals can be useful, but they should be used as a last resort, only when there’s no good way to avoid using them.

除非没有更好的选择才去使用signals

signals are synchronous and blocking 信号是同步阻塞的

使用或者不适用信号

不要使用信号 的情境:

➤ The signal relates to one particular model and can be moved into one of that model’s methods,
possibly called by save()
该信号与某个特定的数据模型相关,可以使用数据模型的方法替代,比如 save() 中

➤ The signal can be replaced with a custom model manager method
该信号能用一个自定义的数据模型管理器方法来替代

➤ The signal relates to a particular view and can be moved into that view.
该信号与某个特定的视图相关,并且能移入那个视图中

可以使用信号的情境:

➤ Your signal receiver needs to make changes to more than one model.
你的信号接收器需要对多个数据模型进行修改

➤ You want to dispatch the same signal from multiple apps and have them handled the same way
by a common receiver.
这个相同的信号需要在多个应用中分发,并且被一个相同的接收器处理

➤ You want to invalidate a cache after a model save.
想在数据模型保存后,用信号使缓存失效

➤ You want to create hooks for a third-party installable app’s interaction with the database. In
some cases this can be a better approach than extending objects.

➤ You have an unusual scenario that needs a callback, and there’s no other way to handle it besides
using a signal. For example, you want to trigger something based on the save() or init()
of a third-party app’s model. You can’t modify the third-party code and extending it might be
impossible, so a signal provides a trigger for a callback.
有一个需要回调的不寻常的场景,并且除了信号无法用其它方式处理。例如,想通过第三方数据模型的 save() 或 init() 来触发。



Use a signal only if the piece of code sending it has positively no way to determine what its
receivers will be

                                           Django core developer Aymeric Augustin

替代使用signals信号

使用自定义数据模型管理器方法来代替信号 model manager

在model 保存之后 post_save, 向管理员发送邮件

# events/managers.py
from django.db import models

class EventManager(models.Manager):

    def create_event(self, title, start, end, creator):
        event = self.model(title=title,
                        start=start,
                        end=end,
                        creator=creator)
        event.save()
        event.notify_admins()
        return event
        
        
# events/models.py
from django.conf import settings
from django.core.mail import mail_admins
from django.db import models

from model_utils.models import TimeStampedModel

from .managers import EventManager

class Event(TimeStampedModel):

    STATUS_UNREVIEWED, STATUS_REVIEWED = (0, 1)
    STATUS_CHOICES = (
        (STATUS_UNREVIEWED, "Unreviewed"),
        (STATUS_REVIEWED, "Reviewed"),
    )

    title = models.CharField(max_length=100)
    start = models.DateTimeField()
    end = models.DateTimeField()
    status = models.IntegerField(choices=STATUS_CHOICES,
                                default=STATUS_UNREVIEWED)
    creator = models.ForeignKey(settings.AUTH_USER_MODEL)

    objects = EventManager()

    def notify_admins(self):
        # create the subject and message
        subject = "{user} submitted a new event!".format(
                        user=self.creator.get_full_name())
        message = """TITLE: {title}
START: {start}
END: {end}""".format(title=self.title, start=self.start,
                    end=self.end)

        # Send to the admins!
        mail_admins(subject=subject,
                    message=message,
                    fail_silently=False)
        


# 使用

>>> from django.contrib.auth import get_user_model
>>> from django.utils import timezone
>>> from events.models import Event
>>> user = get_user_model().get(username="audreyr")
>>> now = timezone.now()
>>> event = Event.objects.create_event(
... title="International Ice Cream Tasting Competition",
... start=now,
... end=now,
... user=user
... )

在其它地方对数据模型进行验证 custom validator

如果现在是通过 pre_save 信号时行验证的,可以尝试为这些数据项编写自定义的验证器。

如果是通过 ModelForm 进行验证,尝试覆盖数据模型的 clean() 方法。

覆盖数据模型的 savedelete 方法 overloaded model method

pre_savepost_save 信号触发的逻辑可以移到数据模型的 save() 方法中。

类似地, 由 pre_deletepost_delete 信号触发的可以移到 delete() 中。

Buy me a 肥仔水!