django: рекурсия с использованием сигнала после сохранения - PullRequest
5 голосов
/ 14 июля 2010

Вот ситуация:

Допустим, у меня есть модель А в Django.Когда я сохраняю объект (класса A), мне нужно сохранить его поля во всех других объектах этого класса.Я имею в виду, что мне нужен любой другой объект, который должен быть скопирован с одного сохраненного объекта.

Когда я использую сигналы (например, после сохранения), я получаю рекурсию (объекты пытаются сохранить друг друга, я думаю) и мой питонУмирает.

Я ожидал, что использование метода .save () для того же класса в сигнале до / после сохранения вызовет рекурсию, но просто не знаю, как этого избежать.

Что мы делаем?

Ответы [ 3 ]

7 голосов
/ 28 ноября 2014

@ ShawnFumo Отключение сигнала опасно, если одна и та же модель сохраняется в другом месте одновременно, не делайте этого!

@ Арам Дулян , ваше решение работает, но мешает вам использовать столь мощные сигналы!

Если вы хотите избежать рекурсии и продолжать использовать сигналы (), простой способ - установить атрибут для текущего экземпляра, чтобы предотвратить срабатывание предстоящих сигналов.

Это можно сделать с помощью простого декоратора, который проверяет, имеет ли данный экземпляр атрибут 'skip_signal' , и в этом случае предотвращает вызов метода:

from functools import wraps

def skip_signal():
    def _skip_signal(signal_func):
        @wraps(signal_func)
        def _decorator(sender, instance, **kwargs):
            if hasattr(instance, 'skip_signal'):
                return None
            return signal_func(sender, instance, **kwargs)  
        return _decorator
    return _skip_signal

Теперь мы можем использовать его следующим образом:

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

@receiver(post_save, sender=MyModel)
@skip_signal()
def my_model_post_save(sender, instance, **kwargs):
    # you processing
    pass

m = MyModel()
# Here we flag the instance with 'skip_signal'
# and my_model_post_save won't be called
# thanks to our decorator, avoiding any signal recursion
m.skip_signal  = True
m.save()

Надеюсь, это поможет.

5 голосов
/ 21 мая 2013

Другой способ справиться с этим - удалить слушателя во время сохранения. Итак:

class Foo(models.Model):
  ...

def foo_post_save(instance):
  post_save.disconnect(foo_post_save, sender=Foo)
  do_stuff_toSaved_instance(instance)
  instance.save()
  post_save.connect(foo_post_save, sender=Foo)

post_save.connect(foo_post_save, sender=Foo)
5 голосов
/ 14 июля 2010

Это будет работать:

class YourModel(models.Model):
    name = models.CharField(max_length=50)

    def save_dupe(self):
        super(YourModel, self).save()

    def save(self, *args, **kwargs):
        super(YourModel, self).save(*args, **kwargs)
        for model in YourModel.objects.exclude(pk=self.pk):
            model.name = self.name
            # Repeat the above for all your other fields
            model.save_dupe()

Если у вас много полей, вы, вероятно, захотите перебрать их при копировании в другую модель. Я оставлю это тебе.

...