← Назад к вопросам

Расскажи как делать транзакции в Django ORM

1.8 Middle🔥 131 комментариев
#Django#Базы данных (SQL)

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Транзакции в Django ORM

Транзакция — это последовательность операций БД, которые либо все выполняются успешно (commit), либо все откатываются (rollback). Django предоставляет несколько способов управления транзакциями.

1. Контекстный менеджер @transaction.atomic()

Наиболее распространённый способ — использовать декоратор или контекстный менеджер:

from django.db import transaction

# Как декоратор
@transaction.atomic
def create_order_with_items(user, items):
    order = Order.objects.create(user=user, total=0)
    total = 0
    for item_id, quantity in items:
        product = Product.objects.get(id=item_id)
        OrderItem.objects.create(
            order=order,
            product=product,
            quantity=quantity,
            price=product.price
        )
        total += product.price * quantity
    order.total = total
    order.save()
    return order

# Как контекстный менеджер
def transfer_funds(from_account, to_account, amount):
    with transaction.atomic():
        from_account.balance -= amount
        from_account.save()
        to_account.balance += amount
        to_account.save()

2. Уровни изоляции

Django поддерживает разные уровни изоляции:

from django.db import transaction

# READ_COMMITTED (по умолчанию в PostgreSQL)
with transaction.atomic():
    user = User.objects.select_for_update().get(id=user_id)
    user.balance -= 100
    user.save()

# SERIALIZABLE (максимальная безопасность)
with transaction.atomic(durable=True):
    # операции
    pass

3. SELECT FOR UPDATE

Для защиты от race conditions используй select_for_update() — это блокирует строку для обновления:

def decrement_inventory(product_id, quantity):
    with transaction.atomic():
        product = Product.objects.select_for_update().get(id=product_id)
        if product.stock >= quantity:
            product.stock -= quantity
            product.save()
            return True
        return False

4. Savepoints

Сохранить частичный откат внутри транзакции:

with transaction.atomic():
    Order.objects.create(user=user, total=100)
    
    sid = transaction.savepoint()  # Сохраняем состояние
    try:
        payment = process_payment(user, 100)
    except PaymentError:
        transaction.savepoint_rollback(sid)  # Откатываемся только платёж
        payment = None
    
    if payment:
        transaction.savepoint_commit(sid)

5. Управление на уровне БД

from django.db import connection, transaction

# Отключить автокоммит
with transaction.atomic(using="default"):
    # вся логика будет в одной транзакции
    pass

# Явный commit/rollback (редко используется)
cursor = connection.cursor()
try:
    cursor.execute("UPDATE accounts SET balance = balance - %s WHERE id = %s", [100, 1])
    connection.commit()  # Явный коммит
except Exception:
    connection.rollback()
    raise

6. Ошибки и лучшие практики

# ❌ Плохо — долгие операции в транзакции
with transaction.atomic():
    order = Order.objects.create(user=user)
    time.sleep(5)  # Блокирует БД
    send_email_async(user)  # Отправка должна быть вне транзакции

# ✅ Хорошо — короткие транзакции, асинхронные операции вне
with transaction.atomic():
    order = Order.objects.create(user=user)

celery_task_send_email.delay(user.id)

# ❌ Плохо — вложенные atomic без savepoints
with transaction.atomic():
    obj1.save()
    with transaction.atomic():  # Новая транзакция создаёт savepoint
        obj2.save()

# ✅ Хорошо — обработка IntegrityError
from django.db import IntegrityError

with transaction.atomic():
    try:
        user = User.objects.create(email=email, username=username)
    except IntegrityError:
        # Email уже существует
        user = User.objects.get(email=email)

7. Проверка статуса

# Проверить, находимся ли в транзакции
if transaction.get_connection().in_atomic_block:
    print("Мы в транзакции")

# Отключить транзакции для конкретного запроса
User.objects.using(read_only).filter(id=1)  # В отдельном соединении

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

  1. Держи транзакции короткими — минимизируй время блокировки
  2. Используй atomic() для атомарности — либо всё, либо ничего
  3. Защищай от race conditions — select_for_update() для конкурентного доступа
  4. Обрабатывай ошибки — IntegrityError, OperationalError
  5. Асинхронные операции вне транзакции — используй Celery для email/API

Транзакции в Django — это основа надёжного взаимодействия с БД, особенно в многопользовательских системах с высокой конкуренцией.

Расскажи как делать транзакции в Django ORM | PrepBro