2011-12-28 5 views
1

У меня есть ситуации, где, когда одна из моих моделей сохраненной MyModel Я хочу проверить поле, и вызвать то же самое изменение в любой другой модели с тем же some_key.Django, post_save recrusion сигнала. Как обойти сигнал обжига

код работает отлично, но его рекурсивно называют сигналы. В результате я теряю вызов 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

Отключить сигнал перед вызовом save, а затем восстановить его впоследствии:

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

Это пугает меня (на случай, если что-то пойдет не так ...) ... но это сработает. – Nix

+0

Что может пойти не так? Единственное, что работает между отключением и подключением - это сохранить, и если это не сработает, вы даже не будете здесь. –

+0

Подсоединение его обратно может пойти не так ... Я думаю, что это подход, его просто попадает в очень страшную категорию. – Nix

3

Раствор может быть польза обновления() метод, чтобы обойти сигнал:

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"

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

Это будет работать, но мы действительно хотим эту логику в сигнальном коде. – Nix

+0

В дополнение к предлагаемым методам, если добавить атрибут модели перед вызовом save, а в обработчике сигнала проверить его. Если атрибут set - пропустить обработку. – demalexx

1

Отключение сигнала не соответствует решение DRY и , например, с помощью обновления() вместо того, чтобы сохранить().

Для обхода сигнала на вашей модели простым способом является установка атрибута в текущем экземпляре для предотвращения срабатывания сигнальных сигналов.

Это можно сделать с помощью простого декоратора, который проверяет, является ли данный экземпляр имеет «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() 

Надежды Это помогает.

+0

Это должен быть принятый ответ, это гораздо более стабильный и масштабируемый способ. – XelharK

Смежные вопросы