2010-07-14 51 views
5

這種情況:django:使用後保存信號遞歸

假設我在django中有一個模型A.當我保存一個對象(類A)時,我需要將它的字段保存到此類的所有其他對象中。我的意思是我需要其他所有的對象都是保存副本。

當我使用信號(例如保存後),我得到一個遞歸(對象試圖保存對方,我猜)和我的python死亡。

我希望在保存前/保存信號的同一類中使用.save()方法會導致遞歸,但不知道如何避免它。

我們該怎麼辦?

+0

也許添加一些代碼讓你的情況變得更清晰了? – adamk 2010-07-14 14:17:42

+0

如果您使用pre_save,將不會發生,因爲您不必自己調用save(),它會在«自然»發生。 – 2014-12-17 11:33:12

回答

4

這將工作:

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

如果你有很多領域,你可能會想將它們複製到其他模型時,在它們之間迭代。我會把它留給你。

+0

當保存的模型是用戶和AFAIK時,它會發生什麼情況,對它進行子類化並不是一個好主意。比方說,我有一個post_save函數與發件人作爲用戶,並在該功能我想更新實例。再次調用save會觸發post_save並停止本地服務器。 – Marconi 2011-02-27 08:57:53

+0

更好的是,不是'model.save_dupe()'而是'super(YourModel,model).save()'。 – 2013-08-20 17:22:52

4

處理此問題的另一種方法是在保存時刪除偵聽器。所以:

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

你救了我的一天,謝謝! – 2014-01-29 11:59:44

7

@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 

現在,我們可以用它這個方式:

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

希望這會有所幫助。

+0

保存後請勿忘記'del skip_signal'。 – 2014-12-17 11:29:12