Что такое вложенные транзакции (subtransactions) в Django и для чего они используются?

Ответ

Вложенная транзакция (subtransaction) в Django — это точка сохранения (savepoint) внутри основной транзакции, управляемая через контекстный менеджер transaction.atomic().

Ключевое отличие: откат вложенной транзакции (например, из-за исключения) не отменяет всю родительскую транзакцию. Он лишь возвращает состояние базы данных к моменту входа во вложенный блок.

Пример:

from django.db import transaction

# Основная транзакция
with transaction.atomic():
    # ... какие-то успешные операции в БД ...

    try:
        # Вложенная транзакция (subtransaction)
        with transaction.atomic():
            # Код, который может вызвать ошибку
            Order.objects.create(status='invalid_data') # Пример операции
            raise ValueError("Ошибка во вложенной транзакции")
    except ValueError:
        # Операции внутри вложенного блока 'atomic' будут отменены,
        # но основная транзакция продолжится.
        print("Вложенная транзакция откатилась.")

    # Этот объект будет успешно сохранен, так как основная транзакция не прервалась
    Product.objects.create(name='Final Product')

Основные сценарии использования:

  • Изоляция части операций: Когда нужно гарантировать атомарность небольшого блока кода, не рискуя всей транзакцией при его сбое.
  • Обработка данных с частичным успехом: Например, при импорте данных из файла, где ошибка в одной строке не должна отменять импорт всех предыдущих корректных строк.

Ответ 18+ 🔞

Давай разберём эту штуку, как будто я тебе на кухне объясняю, с паяльником в одной руке и пивом в другой.

Представь, что ты пишешь важный документ. Вся работа — это одна большая транзакция. Ты начал её, и всё, что ты делаешь, можно в любой момент отменить целиком, нажав Ctrl+Z до самого начала. Страшно, да? А если ты уже полдня писал?

Вот тут и появляется эта хитрая жопа — вложенная транзакция, она же subtransaction. Это как если бы ты внутри своего документа поставил временную точку сохранения. Ты пишешь какой-то рискованный абзац, который может оказаться полной хуйнёй. Если он и правда оказывается хуйнёй, ты откатываешься только до этой точки, а не на начало всего документа! Всё, что было до этого рискованного абзаца, остаётся нетронутым. Гениально, ёпта!

Вот смотри на этот код, он как раз про это:

from django.db import transaction

# Это наша основная, большая работа. Всё внутри этого блока — одна транзакция.
with transaction.atomic():
    # Допустим, тут мы успешно создали пользователя, записали лог. Всё ок.

    try:
        # А вот тут мы ставим нашу временную точку сохранения! Вложенная транзакция.
        with transaction.atomic():
            # Пытаемся сделать что-то опасное. Например, создать заказ с кривыми данными.
            Order.objects.create(status='invalid_data')
            # И тут же, на тебе, — ошибка! Исключение!
            raise ValueError("Ошибка во вложенной транзакции")
    except ValueError:
        # Поймали! Что происходит? Откатилось ВСЁ, что было внутри того вложенного блока `atomic`.
        # Заказ с 'invalid_data' испарился, как будто его и не было.
        print("Вложенная транзакция откатилась. Фух, пронесло.")

    # А это — самое важное. Основная-то транзакция НЕ сломалась!
    # Она жива и может продолжать работу. Этот продукт будет сохранён.
    Product.objects.create(name='Final Product')

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

  1. Чтобы не париться из-за мелочей. Допустим, ты обрабатываешь CSV-файл на тысячу строк. В десятой строке какая-то ерунда, которая ломает логику. Без вложенных транзакций при первой же ошибке откатится ВСЯ тысяча обработанных строк — иди всё по новой, мудак. А с ними — откатится только эта одна кривая строка, а остальные 999 уже будут в базе. Удобно, как хуй с пальто!

  2. Для изоляции говнокода. У тебя есть большой и важный кусок бизнес-логики, а внутри него нужно вызвать какой-то легаси-метод от коллеги-распиздяя, который может в любой момент выстрелить в ногу. Оберни его вызов во вложенный atomic — и если он всё-таки накроется медным тазом, твоя основная логика не пострадает. Просто скажешь "ой, ну бывает" и пойдёшь дальше.

Короче, это как страховочная верёвка при восхождении. Основная транзакция — это весь твой маршрут на гору, а вложенные — это точки страховки. Сорвался на одном сложном участке — повиснешь на последней точке, а не улетишь в хуй-knows-where к подножию. Мудро, Тургенев блядь, отдыхает.