Ответ
Да, сигнал post_save вызывается внутри транзакции, которая оборачивает операцию сохранения модели.
Это означает, что сигнал срабатывает после успешного выполнения SQL-запроса INSERT или UPDATE, но до выполнения COMMIT для всей транзакции.
Ключевые следствия:
- Откат изменений: Если после срабатывания сигнала в том же блоке кода (например, в view) произойдет исключение, вся транзакция будет отменена. Это включает как сохранение самой модели, так и любые изменения в БД, сделанные в обработчике сигнала.
- Недоступность данных: Другие процессы или запросы не увидят изменения в базе данных, пока транзакция не будет закоммичена.
Для выполнения действий, которые должны произойти только после успешного завершения транзакции (например, запуск задачи в 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 для всей этой ебаной транзакции.
Что из этого вытекает, блядь:
- Откат всего нахуй: Если после сигнала, в том же самом блоке кода (ну, во view там), вылезет какое-нибудь исключение — вся транзакция пойдёт нахуй. Откатится. И сохранение модели, и всё, что ты наворотил в обработчике сигнала — всё к ебеням, в рот меня чих-пых!
- Данные в вакууме: Пока транзакция не закоммичена, другие процессы или запросы нихуя не увидят этих изменений в базе. Как будто и не было ничего.
Поэтому, если тебе надо сделать что-то, что должно случиться только после того, как транзакция успешно завершится (ну там, 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)
)