Django,post_save信号重现。如何绕过信号发射

时间:2011-12-28 22:41:12

标签: django google-app-engine signals djangoappengine

我遇到的情况是保存我的某个模型MyModel我想检查一个字段,并在具有相同some_key.

的任何其他模型中触发相同的更改

代码工作正常,但它以递归方式调用信号。结果我浪费了CPU / DB / API调用。我基本上想在.save().任何建议期间绕过信号?

class MyModel(models.Model):
    #bah
    some_field = #
    some_key = #

#in package code __init__.py 
@receiver(models_.post_save_for, sender=MyModel)
def my_model_post_processing(sender, **kwargs):
 # do some unrelated logic...
 logic = 'fun!  '


 #if something has changed... update any other field with the same id
 cascade_update = MyModel.exclude(id=sender.id).filter(some_key=sender.some_key)
 for c in cascade_update:
     c.some_field  = sender.some_field 
     c.save()

4 个答案:

答案 0 :(得分:4)

在致电save之前断开信号,然后重新连接:

post_save.disconnect(my_receiver_function, sender=MyModel)
instance.save()
post_save.connect(my_receiver_function, sender=MyModel)

答案 1 :(得分:3)

解决方案可能是使用update()方法来绕过信号:

cascade_update = MyModel.exclude(
                     id=sender.id).filter(
                     some_key=sender.some_key).update(
                     some_field  = sender.some_field )

"Be aware that the update() method is converted directly to an SQL statement. It is a bulk operation for direct updates. It doesn't run any save() methods on your models, or emit the pre_save or post_save signals"

答案 2 :(得分:1)

断开信号不是 DRY 一致解决方案,例如使用update()而不是save()。

要绕过模型上的信号触发,一个简单的方法是在当前实例上设置一个属性,以防止即将发出的信号。

这可以使用一个简单的装饰器来完成,该装饰器检查给定实例是否具有'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):
    instance.some_field = my_value
    # Here we flag the instance with 'skip_signal'
    # and my_model_post_save won't be called again
    # thanks to our decorator, avoiding any signal recursion
    instance.skip_signal  = True
    instance.save()

希望这有帮助。

答案 3 :(得分:0)

您可以将相关对象的更新代码移动到MyModel.save方法中。那么就不需要玩信号了:

class MyModel(models.Model):
    some_field = #
    some_key = #

    def save(self, *args, **kwargs):
        super(MyModel, self).save(*args, **kwargs)
        for c in MyModel.objects.exclude(id=self.id).filter(some_key=self.some_key):
            c.some_field = self.some_field 
            c.save()