开发者

django: recursion using post-save signal

Here's the situation:

Let's say I have a model A in django. When I'm saving an object (of class A) I need开发者_高级运维 to save it's fields into all other objects of this class. I mean I need every other A object to be copy of lat saved one.

When I use signals (post-save for example) I get a recursion (objects try to save each other I guess) and my python dies.

I men I expected that using .save() method on the same class in pre/post-save signal would cause a recursion but just don't know how to avoid it.

What do we do?


@ShawnFumo Disconnecting a signal is dangerous if the same model is saved elsewhere at the same time, don't do that !

@Aram Dulyan, your solution works but prevent you from using signals which are so powerful !

If you want to avoid recursion and keep using signals (), a simple way to go is to set an attribute on the current instance to prevent upcoming signals firing.

This can be done using a simple decorator that checks if the given instance has the 'skip_signal' attribute, and if so prevents the method from being called:

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

We can now use it this way:

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()

Hope This helps.


This will work:

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()

If you have a lot of fields, you'll probably want to iterate over them when copying them to the other model. I'll leave that to you.


Another way to handle this is to remove the listener while saving. So:

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)
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜