Предотвращение дублирования сигналов для post_save в Django 1.4.3

Я пытаюсь написать код, который отправляет электронное письмо каждый раз, когда один из пользователей изменяет объект модели. В настоящее время я работаю над тем, чтобы один из методов в 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'

Я уже несколько раз перезапускал сервер, сбрасывал/удалял все данные для приложения и продолжаю получать два сигнала. Что-то не так с моим кодом? Любые предложения или идеи будут с благодарностью! Спасибо!


person Kenny    schedule 06.02.2013    source источник
comment
Может быть полезно увидеть код представления/формы, в котором сохраняется объект. Иногда вы можете столкнуться с ситуацией, когда вы фактически сохраняете объект дважды, не осознавая этого.   -  person Michael C. O'Connor    schedule 06.02.2013
comment
Простите меня, если я скажу что-то несоответствующее, я все еще новичок в Django. Код представления/формы, о котором вы говорите, относится к views.py, который создается автоматически при создании приложения, верно? В этом случае мой файл view.py совершенно пустой, нетронутый.   -  person Kenny    schedule 06.02.2013
comment
Итак, вы просто используете администратора Django для изменения своих объектов? Иначе куда вы его сохраняете?   -  person Michael C. O'Connor    schedule 06.02.2013
comment
Да, у меня есть файл admin.py в той же папке приложения. Его содержание буквально состоит из 4 строк. Два импорта моделей и два admin.site.register(model_name). Я использую форму по умолчанию, я полагаю? Тот, у которого 3 варианта сохранения. Что касается того, где я его сохраняю, я думаю, что данные сохраняются в файле test_database.db, который я создал в каталоге проекта.   -  person Kenny    schedule 06.02.2013


Ответы (1)


Ваша проблема связана с тем, что каждый раз, когда вы изменяете объект через интерфейс администратора, приложение администратора создает 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
    
person zifot    schedule 07.02.2013
comment
Я попытался реализовать второе решение, которое вы изложили. isinstance(sender, LogEntry) вернулся как False для обоих сигналов. Я полностью потерян. Может ли быть другая причина двойного сигнала? Я слышал, что дублированный сигнал post_save является результатом того, как Django/Python импортирует модули. - person Kenny; 12.02.2013
comment
Это странно. Я проверял это ранее на Django 1.4, и это было причиной двойного сигнала. Какую версию ты используешь? Просто поместите print sender в обработчик сигнала и давайте просто посмотрим, что вы получите. - person zifot; 13.02.2013
comment
Ах, что вы знаете. Отправитель печати дал мне один сигнал от моих моделей и один сигнал от LogEntry. Ты был прав! Думаю, проблема, вероятно, в том, что мой оператор if во втором решении был написан неправильно. def send_email(sender, **kwargs): if isinstance(sender, LogEntry): напечатать полученный сигнал LogEntry. вернуть еще: - person Kenny; 13.02.2013
comment
Я сделал импорт, используя: from django.contrib.admin.models import LogEntry. Затем в функции send_email я написал оператор if: if isinstance(sender, LogEntry): return, else: #code для отправки электронной почты. Я поместил оператор печати под код оператора else, и он появился дважды при сохранении. Это каким-то образом игнорирует оператор LogEntry if. - person Kenny; 13.02.2013
comment
А, кажется, я понял проблему. Isinstance не работает, потому что оба сигнала относятся к одному и тому же «типу» модели. Однако «значение» отправителя различно для каждого из них. - person Kenny; 13.02.2013