1

我有一种情况,当我的一个模型被保存时,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

4 回答 4

7

通话前断开信号save,然后重新连接:

post_save.disconnect(my_receiver_function, sender=MyModel)
instance.save()
post_save.connect(my_receiver_function, sender=MyModel)
于 2011-12-28T22:45:49.250 回答
5

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

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

这可以使用一个简单的装饰器来完成,该装饰器检查给定实例是否具有“skip_signal”属性,如果有,则阻止调用该方法:

from functools import wraps

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

根据您的示例,这给了我们:

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

希望这可以帮助。

于 2014-11-27T00:18:15.297 回答
3

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

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

“请注意,update() 方法直接转换为 SQL 语句。它是直接更新的批量操作。它不会在模型上运行任何 save() 方法,也不会发出 pre_save 或 post_save 信号”

于 2011-12-28T22:44:43.530 回答
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()
于 2011-12-29T10:23:25.833 回答