Что делает функция transaction.on_commit в Django и когда ее следует использовать

Ответ

Функция transaction.on_commit() в Django позволяет зарегистрировать колбэк (функцию), который будет выполнен только после успешного завершения (коммита) текущей транзакции в базе данных.

Это критически важно для выполнения действий, которые не должны происходить, если транзакция откатится. Основной сценарий использования — запуск фоновых задач, отправка уведомлений или вызов внешних API.

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

Представьте, что вы отправляете приветственное письмо сразу после создания пользователя. Если после отправки письма в транзакции произойдет ошибка и создание пользователя откатится, пользователь получит письмо, хотя его аккаунт не существует. on_commit решает эту проблему.

Пример:

from django.db import transaction
from .tasks import send_welcome_email

def create_user_and_send_email(username, email):
    # Начало транзакции (неявно, если ATOMIC_REQUESTS=True, или явно)
    user = User.objects.create(username=username, email=email)

    # Регистрируем задачу, которая выполнится только после коммита
    transaction.on_commit(lambda: send_welcome_email.delay(user.id))

    # ... другие операции в рамках транзакции
    # Если здесь произойдет ошибка, on_commit() не будет вызван.

Ключевые особенности:

  • Гарантия выполнения: Код выполняется только если транзакция успешно зафиксирована в БД.
  • Изоляция: Позволяет отделить логику работы с БД от побочных эффектов (side effects).
  • Тестирование: Для корректной работы в тестах требует использования django.test.TestCase, который оборачивает каждый тест в транзакцию.

Ответ 18+ 🔞

А, слушай, смотри, это же про transaction.on_commit() в Джанге! Ну, это такая штука, блядь, которая позволяет тебе прицепить какую-нибудь функцию, чтобы она выполнилась только после того, как вся текущая транзакция в базе данных успешно завершится.

Это, сука, архиважно, когда тебе надо что-то сделать, что не должно случиться, если вдруг вся эта кухня с данными откатится нахуй. Типа, запустить фоновую задачу, отправить письмо или дернуть какой-нибудь внешний API.

Ну и зачем это, блядь, вообще нужно?

Представь себе, ты создаёшь пользователя и сразу же, сраный оптимист, отправляешь ему приветственное письмо. А потом, блядь, в транзакции где-то дальше вылезает ошибка, всё откатывается, пользователя в базе нет, а письмо-то уже улетело! Человек получит письмо, а зарегаться не сможет, пиздец. Вот on_commit как раз от таких лютых косяков и спасает.

Вот смотри, как это выглядит:

from django.db import transaction
from .tasks import send_welcome_email

def create_user_and_send_email(username, email):
    # Тут у нас транзакция начинается (сама, если ATOMIC_REQUESTS=True, или ты её явно обернул)
    user = User.objects.create(username=username, email=email)

    # А вот тут мы регистрируем задачу, которая пойдёт в работу ТОЛЬКО если всё закоммитится
    transaction.on_commit(lambda: send_welcome_email.delay(user.id))

    # ... а тут дальше можешь ещё какую-нибудь хуйню в рамках транзакции делать
    # И если тут что-то пойдёт не так, то `on_commit` не выстрелит, и письмо не улетит!

Главные фишки, блядь:

  • Железная гарантия: Твой код выполнится только если транзакция прошла успешно, и данные реально легли в базу. Иначе — ни хуя.
  • Изоляция: Позволяет красиво отделить основную бизнес-логику работы с базой от всяких побочных эффектов, которые могут наебнуться.
  • Тестирование: Тут, сука, внимание! Чтобы это в тестах работало как надо, нужно использовать django.test.TestCase. Он каждый тест в транзакцию заворачивает, иначе можно охуеть от неожиданных результатов.