防止Django 1.4.3中的post_save重复信号

4 投票
1 回答
3166 浏览
提问于 2025-04-17 15:00

我正在尝试写一些代码,让每当用户修改一个模型对象时,就发送一封邮件。目前,我在努力让models.py中的一个方法接收post_save信号。我知道post_save信号通常会发送两次,因此,解决这个问题的方法是使用dispatch_uid参数。我已经这样做了,但出于某种奇怪的原因,我仍然收到了两个信号。以下是我应用程序的model.py文件中的代码。

from django.db import models
from django.db.models.signals import post_save

def send_email(sender, **kwargs):
      print "Signal sent." #just a placeholder

post_save.connect(send_email, dispatch_uid="unique_identifier")

class Library_Associates (models.Model):
      first_name = models.CharField(max_length = 200)
      last_name = models.CharField(max_length = 200)

  department_choices = (
        ('ENG', 'Engineering'),
        ('ART', 'Arts and Sciences'),
        ('AFM', 'Accounting and Financial Managment'),
        ('MAT', 'Mathematics'),
  )

  department = models.CharField(max_length = 3, choices = department_choices, default = 'ENG')

  pub_date = models.DateTimeField ('date published')

  def __unicode__(self):
        return self.first_name

  class Meta:
        verbose_name_plural = 'Library Associates'

class Info_Desk_Staff (models.Model):
      first_name = models.CharField(max_length=50)
      last_name = models.CharField(max_length=50)
      salary = models.IntegerField()
      hours_worked = models.IntegerField()

      def __unicode__(self):
            return self.first_name

      class Meta:
            verbose_name_plural = 'Info Desk Staff'

我已经重启了服务器好几次,重置/删除了应用程序的所有数据,但我仍然收到了两个信号。我的代码是否存在根本性的问题?任何建议或见解都将非常感激!谢谢!

1 个回答

6

你的问题出在每次通过管理界面修改对象时,管理应用会创建一个叫做 django.contrib.admin.models.LogEntry 的实例,这个实例用来记录所做的更改。

因为你在所有对象上都监听了 post_save 事件,所以你的监听器会被调用两次——第一次是针对你的模型,第二次是针对 LogEntry 模型。

可能的解决方案包括:

  1. 为每个模型单独注册你的监听器(例如,可以选择你的模型,然后在一个循环中进行注册),使用 sender 参数在 post_save 方法中。

    for model in get_models():
        post_save.connect(send_email, sender = model, dispatch_uid='unique_identifier')
    
  2. 检查发送给监听器的 sender 是否不是 django.contrib.admin.models.LogEntry 的实例。

    from django.contrib.admin.models import LogEntry
    ...
    
    def send_email(sender, **kwargs):
        if isinstance(sender, LogEntry):
            return
    
  3. 给你的模型一个共同的父类,并在监听器中使用这个父类进行测试。

    class MyModel(models.Model):
        pass
    
    class Library_Associates (MyModel):
        ...
    class Info_Desk_Staff (MyModel):
        ...
    
    def send_email(sender, **kwargs):
        if not isinstance(sender, MyModel):
            return
    

撰写回答