Как управлять транзакциями в SQLAlchemy

Ответ

Транзакции в SQLAlchemy обеспечивают атомарность операций с базой данных (принцип ACID). Управлять ими можно несколькими способами, но предпочтительным является использование контекстных менеджеров.

1. Рекомендуемый способ: Контекстный менеджер Session.begin()

Это самый надежный и лаконичный подход. Он автоматически выполняет commit() при успешном выходе из блока или rollback() при возникновении исключения.

from sqlalchemy.orm import Session

# session создается ранее

with Session(engine) as session:
    try:
        with session.begin():
            # Все операции внутри этого блока — часть одной транзакции
            user = User(name='John Doe')
            session.add(user)
            # ... другие операции
        # Здесь транзакция автоматически закоммитится
    except Exception as e:
        # Здесь транзакция автоматически откатится
        print(f"Transaction failed: {e}")

2. Явное управление через Session

Классический подход, требующий ручного вызова commit() и rollback().

from sqlalchemy.orm import sessionmaker

Session = sessionmaker(bind=engine)
session = Session()

try:
    user = User(name='Jane Doe')
    session.add(user)
    session.commit() # Явный коммит
except:
    session.rollback() # Явный откат в случае ошибки
    raise
finally:
    session.close() # Важно закрывать сессию

3. Уровень Core: Контекстный менеджер Engine.begin()

Подходит для выполнения низкоуровневых SQL-запросов без использования ORM-сессий.

with engine.begin() as connection:
    connection.execute(text("UPDATE users SET active=true WHERE id=1"))
    # Автоматический commit или rollback

Ключевые принципы:

  • Атомарность: Всегда используйте транзакции для операций, которые должны быть выполнены как единое целое (например, перевод денег со счета на счет).
  • Безопасность: Контекстные менеджеры (with ...) — лучший способ избежать незакрытых сессий или незавершенных транзакций.
  • Вложенные транзакции: Для более сложных сценариев можно использовать session.begin_nested().

Ответ 18+ 🔞

А, ну вот, опять про эти ваши транзакции, блядь! Слушай, давай я тебе на пальцах, как для дебила, объясню, а то ты, сука, опять накосячишь и базу мне похеришь.

Представь, что ты в кабаке заказываешь пиво и чипсы. Транзакция — это когда ты либо и пиво, и чипсы получил, либо нихуя. Не может быть такого, чтобы ты пиво выпил, а чипсы тебе забыли принести. Это пиздец, а не заказ. Вот SQLAlchemy как раз за этим и следит, чтобы не было такого распиздяйства.

Способ первый, нормальный (через Session.begin())

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

from sqlalchemy.orm import Session

# Допустим, у тебя уже есть engine, как бутылка водки в баре

with Session(engine) as session: # Зашёл в бар
    try:
        with session.begin(): # Сказал официанту "я хочу вот это"
            # Всё, что тут делаешь — это один заказ
            user = User(name='Вася Пупкин')
            session.add(user) # "И пиво, и чипсы!"
            # ... другие операции
        # Тут официант сам тебе всё принёс и счёт отдал (автокоммит)
    except Exception as e:
        # А тут кухня взорвалась, и он говорит: "Извини, братан, нихуя не будет" (автороллбэк)
        print(f"Всё пропало, шеф: {e}")

Способ второй, ручной (как в подвальном баре)

Тут ты сам себе официант, бармен и кассир. Забыл что-то сделать — сидишь трезвый и голодный. Надо всё руками: сказать, что хочешь, заплатить, а если что-то пошло не так — самому деньги из кассы забрать. Страшно и неудобно.

from sqlalchemy.orm import sessionmaker

Session = sessionmaker(bind=engine)
session = Session() # Зашёл в этот тёмный подвал

try:
    user = User(name='Жанна Дарк')
    session.add(user) # Крикнул бармену заказ
    session.commit() # А теперь, сука, беги на кухню, готовь и приноси счёт! (явный коммит)
except:
    session.rollback() # Ой, всё, бармен сдох. Быстро забирай свои деньги из кассы и беги! (явный откат)
    raise
finally:
    session.close() # И дверь за собой, мудак, закрой, сквозняк!

Способ третий, для гурманов (через Engine.begin())

Это когда ты вообще мимо бара прошёл, зашёл прямо на склад, взял ящик пива и паллету чипсов. Без официантов, без сессий, голыми руками. Для сложных, мужицких операций.

with engine.begin() as connection: # Пролез на склад через чёрный ход
    connection.execute(text("UPDATE users SET active=true WHERE id=1")) # Взял что надо
    # Ушёл — и автоматически сигнализация сработала (коммит) или охрана тебя поймала (роллбэк)

Запомни, балбес:

  • Атомарность — это когда либо всё, либо нихуя. Как с тем пивом и чипсами.
  • Контекстные менеджеры (with ...) — это твои лучшие друзья. Они за тебя всё закроют, откатят и приберутся. Не будь идиотом, используй их.
  • Вложенные транзакции (session.begin_nested()) — это когда ты внутри большого заказа делаешь ещё один маленький. Типа "я ещё и кальян хочу". Сложная хуйня, без надобности не лезь.

Вот и вся наука. Не усложняй, пиши код так, чтобы его не стыдно было показать, а то опять накосячишь, и мне за тебя краснеть.