Django, post_save - восстановление сигнала. Как обойти сигнал стрельбы - PullRequest
1 голос
/ 29 декабря 2011

У меня есть ситуация, когда одна из моих моделей сохраняется 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 голосов
/ 29 декабря 2011

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

post_save.disconnect(my_receiver_function, sender=MyModel)
instance.save()
post_save.connect(my_receiver_function, sender=MyModel)
3 голосов
/ 29 декабря 2011
1 голос
/ 27 ноября 2014

Отключение сигнала не является решением 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()

Надеюсь, что это помогает.

0 голосов
/ 29 декабря 2011

Вы можете переместить код обновления связанных объектов в метод 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()
...