Я написал несколько API, для которых соответствующие функции выполняются внутри блока транзакции. Я вызываю метод save() (после некоторых модификаций) для экземпляра / нескольких / нескольких моделей, а также последовательно индексирую некоторую связанную с JSON информацию экземпляра / ов в Elasticsearch . Я хочу, чтобы база данных выполняла откат, даже если по какой-то причине save() для одного из экземпляров или индексация в Elasticsearch не удается.

Теперь возникает проблема, заключающаяся в том, что даже внутри блока транзакции вызываются сигналы post_save(), и это проблема, потому что некоторые уведомления запускаются из этих сигналов.

Есть ли способ активировать сигналы post_save() только после успешного завершения транзакции?

26
doto 17 Окт 2015 в 01:56

3 ответа

Лучший ответ

На самом деле, нет. Сигналы не имеют ничего общего с успехом или неудачей транзакции db, но с самим методом save - перед вызовом у вас запускается сигнал pre_save, а после вызова у вас запускается сигнал post_save.

Здесь есть 2 подхода:

  • вы собираетесь проверить экземпляр в методе post_save и решить, успешно ли сохранена модель; Самый простой способ сделать это: в методе save после успешного выполнения транзакции аннотируйте свой экземпляр флагом, скажем instance.saved_successfully = True, который вы протестируете в обработчике post_save.
  • вы собираетесь отказаться от сигнала post_save и создать для себя собственный сигнал, который вы активируете после успешного выполнения транзакции.

Имеет смысл?

P.S.

Если вам необходимо выполнить привязку к сигналу фиксации транзакции, ознакомьтесь с этим пакетом: https : //django-transaction-hooks.readthedocs.org/en/latest/; похоже, что функциональность интегрирована в Django 1.9a.

7
Roba 19 Окт 2015 в 06:01

У меня были серьезные проблемы с администратором django, который не разрешал транзакции post_save для родительских объектов, когда у них были изменены встроенные дочерние элементы.

Это было моим решением ошибки с жалобой на выполнение запросов в середине атомарного блока:

def on_user_post_save_impl(user):
     do_something_to_the_user(user)

def on_user_post_save(sender, instance, **kwargs):
    if not transaction.get_connection().in_atomic_block:
        on_user_post_save_impl(instance)
    else:
        transaction.on_commit(lambda: on_user_post_save_impl(instance))
5
Chris Berry 10 Июл 2018 в 00:01

Я думаю, что самый простой способ - использовать транзакцию . on_commit () . Вот пример использования подкласса models.Model Photo, который будет взаимодействовать с Elasticsearch только после завершения текущей транзакции:

from django.db import transaction
from django.db.models.signals import post_save

@receiver(post_save, sender=Photo)
def save_photo(**kwargs):
    transaction.on_commit(lambda: talk_to_elasticsearch(kwargs['instance']))

Обратите внимание, что если transaction.on_commit() выполняется, не находясь в активной транзакции, он запускается немедленно.

23
Michael Tom 8 Окт 2018 в 13:18