Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
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 — это мощный механизм для обеспечения частичной откатываемости операций. Правильное использование спасает от потери данных и позволяет создавать надёжные приложения с комплексной бизнес-логикой.