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

Сталкивался ли с Subtransaction в Django

2.3 Middle🔥 141 комментариев
#Django

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

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

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

Subtransaction в Django: Практический Опыт

Да, я регулярно работаю с Subtransaction в Django при разработке сложных бизнес-логик с множественными операциями в БД. Это критически важный инструмент для обеспечения целостности данных.

Что такое Subtransaction

Subtransaction (сохранная точка, savepoint) — это механизм, который позволяет откатить часть операций внутри основной транзакции, не откатывая всю транзакцию целиком. Это достигается через создание точек сохранения (savepoints) в БД.

Различие между Transactio и Subtransaction

from django.db import transaction

# Обычная транзакция — всё или ничего
with transaction.atomic():
    user = User.objects.create(username="john")
    profile = Profile.objects.create(user=user, bio="Test")  # Если ошибка, всё откатится
# Subtransaction — откатываем только часть
with transaction.atomic():
    user = User.objects.create(username="john")
    
    try:
        with transaction.atomic():
            profile = Profile.objects.create(user=user, bio="Test")
            # Вспомогательная операция с риском
            process_profile(profile)  # Если упадёт, откатится только она
    except Exception as e:
        # user остаётся в БД, profile нет
        logger.error(f"Profile creation failed: {e}")
        # Продолжаем работу с основной транзакцией

Практический Пример: Обработка Платежей

Лучший пример использования Subtransaction — обработка платежей со сложной логикой:

from django.db import transaction

def process_order(order_id):
    with transaction.atomic():
        order = Order.objects.select_for_update().get(id=order_id)
        order.status = "processing"
        order.save()
        
        # Основная операция — вычитаем средства
        wallet = Wallet.objects.select_for_update().get(user=order.user)
        if wallet.balance < order.total:
            raise InsufficientFundsError()
        
        wallet.balance -= order.total
        wallet.save()
        
        # Subtransaction для опциональных операций
        try:
            with transaction.atomic():
                # Отправляем уведомление (может быть RabbitMQ ошибка)
                send_order_notification(order)
                # Обновляем статистику
                update_user_stats(order.user)
        except Exception as e:
            # Ошибка в уведомлении не должна ломать заказ
            logger.warning(f"Notification failed: {e}")
        
        order.status = "completed"
        order.save()
        
        return order

Использование Savepoints Напрямую

Для более гранулярного контроля можно использовать savepoints напрямую:

from django.db import transaction

def batch_update_users(users):
    with transaction.atomic():
        for user in users:
            sid = transaction.savepoint()  # Создаём сохранённую точку
            
            try:
                user.is_active = True
                user.full_clean()  # Валидация может упасть
                user.save()
            except ValidationError as e:
                transaction.savepoint_rollback(sid)  # Откатываем только этого юзера
                logger.error(f"User {user.id} validation failed: {e}")
                continue
            
            transaction.savepoint_commit(sid)  # Коммитим успешное обновление

Когда Использовать Subtransaction

  • Обработка платежей — отката опциональных операций не должно ломать платёж
  • Пакетная обработка — когда нужна частичная откат при ошибках
  • Интеграции с внешними сервисами — сбой API не должен ломать локальные данные
  • Многошаговые операции — когда некоторые шаги можно повторить

Важные Моменты

SELECT FOR UPDATE при вложенных транзакциях:

with transaction.atomic():
    user = User.objects.select_for_update().get(id=user_id)
    
    with transaction.atomic():
        # Блокировка сохраняется в Subtransaction
        user.balance += 100
        user.save()

Исключение при повторном коммите:

with transaction.atomic():
    try:
        with transaction.atomic():
            # Некая операция
            pass
    except:
        # Нельзя коммитить savepoint за пределами контекста
        pass  # Автоматически откатится

Заключение

Subtransaction в Django — это мощный механизм для обеспечения частичной откатываемости операций. Правильное использование спасает от потери данных и позволяет создавать надёжные приложения с комплексной бизнес-логикой.