django:使用保存后信号的递归

gru*_*cha 5 django recursion signals save

情况如下:

假设我在django中有一个模型A. 当我保存一个对象(A类)时,我需要将它的字段保存到该类的所有其他对象中.我的意思是我需要每个其他A对象都是lat保存的副本.

当我使用信号(例如后保存)时,我得到一个递归(对象试图相互保存,我猜)并且我的python死了.

我希望在前/后保存信号中对同一个类使用.save()方法会导致递归,但只是不知道如何避免它.

我们做什么?

Cha*_*thk 8

@ShawnFumo如果同一个模型同时保存在别处,则断开信号是危险的,不要这样做!

@Aram Dulyan,你的解决方案有效,但阻止你使用如此强大的信号!

如果你想避免递归并继续使用signals(),一个简单的方法是在当前实例上设置一个属性,以防止即将发出的信号.

这可以使用一个简单的装饰器来完成,该装饰器检查给定的实例是否具有'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
Run Code Online (Sandbox Code Playgroud)

我们现在可以这样使用它:

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()
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.


Ara*_*yan 5

这将起作用:

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()
Run Code Online (Sandbox Code Playgroud)

如果您有很多字段,则在将它们复制到其他模型时可能需要对其进行迭代。我留给你。


Sha*_*umo 5

处理此问题的另一种方法是在保存时删除侦听器.所以:

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)
Run Code Online (Sandbox Code Playgroud)