Ответ
Для обеспечения атомарности транзакций в Django используется механизм django.db.transaction.atomic()
. Он гарантирует, что блок кода будет выполнен как единая, неделимая операция: либо все изменения внутри блока будут успешно применены к базе данных (commit), либо, в случае возникновения любой ошибки, все изменения будут полностью отменены (rollback). Это критически важно для поддержания целостности данных.
atomic()
может использоваться как декоратор для функций или как контекстный менеджер.
1. Использование как декоратора: Применяется к функции, которая должна выполняться атомарно. Если внутри функции возникает исключение, транзакция откатывается.
from django.db import transaction
from myapp.models import Account
@transaction.atomic
def transfer_funds(sender_id, receiver_id, amount):
sender = Account.objects.select_for_update().get(id=sender_id)
receiver = Account.objects.select_for_update().get(id=receiver_id)
if sender.balance < amount:
raise ValueError("Недостаточно средств на счете отправителя")
sender.balance -= amount
receiver.balance += amount
sender.save()
receiver.save()
# Если здесь произойдет ошибка, все изменения будут отменены
# Пример вызова:
# try:
# transfer_funds(1, 2, 100)
# except ValueError as e:
# print(f"Ошибка транзакции: {e}")
2. Использование как контекстного менеджера: Позволяет явно определить блок кода, который должен быть атомарным. Это удобно для более гранулярного контроля или когда атомарность нужна только для части функции.
from django.db import transaction
from myapp.models import Order, Product
def process_order(order_id, product_ids):
with transaction.atomic():
order = Order.objects.get(id=order_id)
total_price = 0
for product_id in product_ids:
product = Product.objects.select_for_update().get(id=product_id)
if product.stock < 1:
raise ValueError(f"Товар {product.name} отсутствует на складе")
product.stock -= 1
product.save()
total_price += product.price
order.total_price = total_price
order.status = 'processed'
order.save()
# Если внутри этого блока произойдет исключение, все изменения будут отменены
# Пример вызова:
# try:
# process_order(1, [101, 102])
# except ValueError as e:
# print(f"Ошибка обработки заказа: {e}")
Вложенные транзакции и точки сохранения (Savepoints):
Django поддерживает вложенные atomic()
блоки. Вложенный блок создает точку сохранения (savepoint). Если внутренний блок завершается с ошибкой, откатываются только изменения до этой точки сохранения, а внешний блок может продолжить работу или обработать исключение. Если внешний блок завершается с ошибкой, откатывается вся транзакция.
from django.db import transaction
with transaction.atomic(): # Внешняя транзакция
# ... какие-то операции ...
with transaction.atomic(): # Вложенная транзакция (создает savepoint)
# ... операции, которые могут быть отменены отдельно ...
# raise ValueError("Ошибка во вложенной транзакции")
# ... операции после вложенной транзакции ...
Использование transaction.atomic()
является стандартной и рекомендуемой практикой для обеспечения надежности и согласованности данных в Django-приложениях.