Django post_save предотвращает рекурсию без переопределения сохранения модели()
Существует много сообщений о переполнении стека о рекурсии с использованием сигнала post_save
, на который комментарии и ответы подавляюще: "почему бы не переопределить save()" или сохранение, которое запускается только при created == True
.
Ну, я считаю, что хороший пример для использования save()
- например, я добавляю временное приложение, которое обрабатывает данные о выполнении заказов, полностью отделенные от нашей модели заказа.
Остальная часть фреймворка блаженно не осознает приложение исполнения, и использование hook_stats_save изолирует весь код, связанный с выполнением, от нашей модели заказа.
Если мы откажемся от службы исполнения, ничего о нашем базовом коде не изменится. Мы удаляем приложение для выполнения и что оно.
Итак, существуют ли приличные методы для обеспечения того, чтобы сигнал post_save не запускал один и тот же обработчик дважды?
Ответы
Ответ 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