Вызывается ли сигнал post_save в Django внутри транзакции базы данных

Ответ

Да, сигнал post_save вызывается внутри транзакции, которая оборачивает операцию сохранения модели.

Это означает, что сигнал срабатывает после успешного выполнения SQL-запроса INSERT или UPDATE, но до выполнения COMMIT для всей транзакции.

Ключевые следствия:

  1. Откат изменений: Если после срабатывания сигнала в том же блоке кода (например, в view) произойдет исключение, вся транзакция будет отменена. Это включает как сохранение самой модели, так и любые изменения в БД, сделанные в обработчике сигнала.
  2. Недоступность данных: Другие процессы или запросы не увидят изменения в базе данных, пока транзакция не будет закоммичена.

Для выполнения действий, которые должны произойти только после успешного завершения транзакции (например, запуск задачи в Celery или отправка уведомления), следует использовать transaction.on_commit().

Пример:

from django.db import transaction
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import MyModel

# Этот обработчик выполняется ВНУТРИ транзакции
@receiver(post_save, sender=MyModel)
def my_signal_handler(sender, instance, created, **kwargs):
    if created:
        print(f"Объект {instance.pk} создан, но транзакция еще не закоммичена.")

        # Эта задача запустится только ПОСЛЕ успешного COMMIT
        transaction.on_commit(
            lambda: some_celery_task.delay(instance.pk)
        )

Ответ 18+ 🔞

Ага, вот смотри, про этот твой post_save сигнал. Он, сука, срабатывает внутри транзакции, которая оборачивает всё это сохранение модели.

То есть, представь: запрос в базу улетел, INSERT или UPDATE там, всё прошло, сигнал уже орёт «готово!», но до того самого COMMIT для всей этой ебаной транзакции.

Что из этого вытекает, блядь:

  1. Откат всего нахуй: Если после сигнала, в том же самом блоке кода (ну, во view там), вылезет какое-нибудь исключение — вся транзакция пойдёт нахуй. Откатится. И сохранение модели, и всё, что ты наворотил в обработчике сигнала — всё к ебеням, в рот меня чих-пых!
  2. Данные в вакууме: Пока транзакция не закоммичена, другие процессы или запросы нихуя не увидят этих изменений в базе. Как будто и не было ничего.

Поэтому, если тебе надо сделать что-то, что должно случиться только после того, как транзакция успешно завершится (ну там, Celery-задачу запустить или письмо отправить), надо использовать transaction.on_commit(). Это, блядь, ключевой момент!

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

from django.db import transaction
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import MyModel

# Этот обработчик работает ВНУТРИ транзакции, помни это!
@receiver(post_save, sender=MyModel)
def my_signal_handler(sender, instance, created, **kwargs):
    if created:
        print(f"Объект {instance.pk} создан, но транзакция еще не закоммичена, ёпта.")

        # А вот эта штука запустится только ПОСЛЕ того, как COMMIT пройдёт успешно
        transaction.on_commit(
            lambda: some_celery_task.delay(instance.pk)
        )