1. transaction.on_commit() что это?

Это метод регистрации обратного вызова, который используется, когда необходимо выполнить какую-либо операцию сразу после успешного завершения транзакции в Django.

from django.db import transaction

transaction.on_commit(my_function)

Когда этот код выполняется, my_function() будет вызвана после успешного завершения текущей транзакции.

Django transaction.on_commit conceptual image


2. В каких ситуациях используется?

Ситуация Использовать?
Когда нужно что-то выполнить сразу после завершения сохранения в БД ✅ Подходит
Когда нужно выполнить задачу Celery после транзакции ✅ Подходит
Внешние API вызовы, обновления кэша и тому подобное ✅ Подходит
Когда порядок вызовов вне транзакции не имеет значения ❌ Неподходит

3. Основные понятия

  • on_commit() имеет значение только когда есть текущая выполняемая транзакция.
  • Если транзакции нет, обратный вызов выполнится немедленно!

4. Неправильный пример

def my_view(request):
    def notify():
        print("Выполнено!")

    transaction.on_commit(notify)  # Транзакции нет, поэтому выполняется немедленно!

5. Правильный пример использования

from django.db import transaction

def my_view(request):
    with transaction.atomic():
        post = Post.objects.create(...)
        post.categories.add(...)

        def translate():
            translate_post.delay(post.id)

        transaction.on_commit(translate)

В данном примере translate() будет выполнен только после успешного создания post и добавления категорий.


6. Почему это важно?

Многие разработчики путают следующее:

  • create(), add() ORM фактически не являются завершёнными коммитами
  • До коммита все в состоянии "логического сохранения".
  • Задачи Celery выполняются в отдельном процессе, и
  • если они выполняются до коммита, они будут ссылаться на некорректные данные.

7. Идеальный пример использования с Celery

@shared_task
def post_finalize_task(post_id, categories, tags):
    post = Post.objects.get(id=post_id)

    with transaction.atomic():
        for name in categories:
            cat, _ = Category.objects.get_or_create(name=name)
            post.categories.add(cat)

        for name in tags:
            tag, _ = Tag.objects.get_or_create(name=name)
            post.tags.add(tag)

        def schedule_translation():
            translate_post.delay(post.id)

        transaction.on_commit(schedule_translation)

Эта структура гарантирует, что задача translate_post будет запущена только после успешного сохранения категорий и тегов.


8. Итоговый обзор

Понятие Описание
Когда выполняется? Сразу после успешного завершения транзакции
При неудаче? Никогда не выполняется при откате
Если транзакции нет? Выполняется немедленно (внимание!)
Где использовать? Внутри блока with transaction.atomic()
Примеры использования Выполнение задач Celery, отправка уведомлений, обновление кэша и т.д.

9. Комментарий Джесси

transaction.on_commit() — это не просто отложенное выполнение.
Это четкое выражение намерения разработчика выполнять код только в случае успешного завершения транзакции, и это необходимый инструмент для поддержания целостности данных при использовании ORM и асинхронной обработки.