Подтвердить что ты не робот

Django post_save предотвращает рекурсию без переопределения сохранения модели()

Существует много сообщений о переполнении стека о рекурсии с использованием сигнала post_save, на который комментарии и ответы подавляюще: "почему бы не переопределить save()" или сохранение, которое запускается только при created == True.

Ну, я считаю, что хороший пример для использования save() - например, я добавляю временное приложение, которое обрабатывает данные о выполнении заказов, полностью отделенные от нашей модели заказа.

Остальная часть фреймворка блаженно не осознает приложение исполнения, и использование hook_stats_save изолирует весь код, связанный с выполнением, от нашей модели заказа.

Если мы откажемся от службы исполнения, ничего о нашем базовом коде не изменится. Мы удаляем приложение для выполнения и что оно.

Итак, существуют ли приличные методы для обеспечения того, чтобы сигнал post_save не запускал один и тот же обработчик дважды?

4b9b3361

Ответ 1

Что вы думаете об этом решении?

@receiver(post_save, sender=Article)
def generate_thumbnails(sender, instance=None, created=False, **kwargs):

    if not instance:
        return

    if hasattr(instance, '_dirty'):
        return

    do_something()

    try:
        instance._dirty = True
        instance.save()
    finally:
        del instance._dirty

Вы также можете создать декоратор

def prevent_recursion(func):

    @wraps(func)
    def no_recursion(sender, instance=None, **kwargs):

        if not instance:
            return

        if hasattr(instance, '_dirty'):
            return

        func(sender, instance=instance, **kwargs)

        try:
            instance._dirty = True
            instance.save()
        finally:
            del instance._dirty

    return no_recursion


@receiver(post_save, sender=Article)
@prevent_recursion
def generate_thumbnails(sender, instance=None, created=False, **kwargs):

    do_something()

Ответ 2

вы можете использовать обновление вместо сохранения в обработчике сигнала

quersyset.filter(pk=instance.pk).update(....)

Ответ 3

Не отсоединяйте сигналы. Если какая-либо новая модель того же типа генерируется, когда сигнал отключен, функция обработчика не будет запущена. Сигналы являются глобальными в Django, и несколько запросов могут выполняться одновременно, что приводит к сбою, а другие запускают обработчик post_save.

Ответ 4

Я думаю, что создание метода save_without_signals() в модели более явственно:

class MyModel()
    def __init__():
        # Call super here.
        self._disable_signals = False

    def save_without_signals(self):
        """
        This allows for updating the model from code running inside post_save()
        signals without going into an infinite loop:
        """
        self._disable_signals = True
        self.save()
        self._disable_signals = False

def my_model_post_save(sender, instance, *args, **kwargs):
    if not instance._disable_signals:
        # Execute the code here.

Ответ 5

Как отключить, а затем повторно подключить сигнал в вашей функции post_save:

def my_post_save_handler(sender, instance, **kwargs):
    post_save.disconnect(my_post_save_handler, sender=sender)
    instance.do_stuff()
    instance.save()
    post_save.connect(my_post_save_handler, sender=sender)
post_save.connect(my_post_save_handler, sender=Order)

Ответ 6

Вы должны использовать queryset.update() вместо Model.save(), но вам нужно позаботиться о чем-то другом:

Важно отметить, что когда вы используете его, если вы хотите использовать новый объект, вы должны снова получить его объект, потому что он не изменит сам объект, например:

>>> MyModel.objects.create(pk=1, text='')
>>> el = MyModel.objects.get(pk=1)
>>> queryset.filter(pk=1).update(text='Updated')
>>> print el.text
>>> ''

Итак, если вы хотите использовать новый объект, вы должны сделать еще раз:

>>> MyModel.objects.create(pk=1, text='')
>>> el = MyModel.objects.get(pk=1)
>>> queryset.filter(pk=1).update(text='Updated')
>>> el = MyModel.objects.get(pk=1) # Do it again
>>> print el.text
>>> 'Updated'

Ответ 7

Вы также можете проверить аргумент raw в post_save, а затем вызвать save_base вместо save.

Ответ 8

Проверь это...

Каждый сигнал имеет свои преимущества, о которых вы можете прочитать в документации здесь, но я хотел бы поделиться несколькими вещами, о которых следует помнить с сигналами pre_save и post_save.

  • Оба вызываются каждый раз .save() в модели вызывается. Другими словами, если вы сохраните экземпляр модели, сигналы будут отправлены.

  • Выполнение save() для экземпляра в post_save часто может создать бесконечный цикл и, следовательно, привести к превышению максимальной глубины рекурсии - только если вы неправильно используете .save().

  • pre_save отлично подходит для изменения только данных экземпляра, потому что вам не нужно вызывать save(), что исключает возможность выше. Причина, по которой вам не нужно вызывать save(), заключается в том, что сигнал pre_save буквально означает "прямо перед сохранением".

  • Сигналы могут вызывать другие сигналы и/или запускать отложенные задачи (для сельдерея), которые могут быть огромными для удобства использования.

Источник: https://www.codingforentrepreneurs.com/blog/post-save-vs-pre-save-vs-override-save-method/

С уважением!!

Ответ 9

читайте здесь с помощью Bulk_Create, который вы можете сохранить без пост-сохранения и предварительного сохранения.

Django Docs