Как реализовать атомарную транзакцию для API эндпоинта в Django?

Ответ

Для обеспечения атомарности операций с базой данных в рамках одного API-запроса в Django используется декоратор transaction.atomic.

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

Для Function-Based Views (FBV)

Декоратор применяется непосредственно к функции представления.

from django.db import transaction
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status

@api_view(['POST'])
@transaction.atomic
def create_order(request):
    try:
        # Пример: создание заказа и списание товара со склада
        order = create_order_in_db(request.data)
        update_stock_count(order.product, order.quantity)

        # Все операции с БД здесь атомарны
        return Response({'status': 'success'}, status=status.HTTP_201_CREATED)

    except Exception as e:
        # При любом исключении транзакция автоматически откатится
        return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)

Для Class-Based Views (CBV)

Для применения транзакций к методам в классовых представлениях (например, post, put) используется method_decorator:

from django.db import transaction
from django.utils.decorators import method_decorator
from rest_framework.views import APIView
from rest_framework.response import Response

class OrderAPIView(APIView):
    @method_decorator(transaction.atomic)
    def post(self, request, *args, **kwargs):
        # Логика, требующая транзакционности, находится здесь
        # ...
        return Response({'status': 'success'})

Также можно применить декоратор ко всему классу через dispatch, чтобы все HTTP-методы выполнялись в транзакции: @method_decorator(transaction.atomic, name='dispatch').

Ответ 18+ 🔞

Ах ты ж ёпта, ну и тема подъехала! Слушай, смотри, чтобы у тебя в базе данных всё было чётко, как у швейцарских часов, а не как в моём гараже после субботней пьянки, в Django есть такая штука — transaction.atomic.

Вот представь: ты делаешь кучу операций с базой — создаёшь заказ, списываешь товар, обновляешь баланс. И если на третьем шаге что-то пошло по пизде, например, товара не хватило, то первые два шага должны откатиться нахуй, как будто их и не было. Иначе получится, что заказ создан, а товар не списан — и ты уже в жопе, клиент орёт, бухгалтер плачет. Вот atomic как раз и следит, чтобы либо всё прошло идеально, либо нихуя не сохранилось. Целостность данных, блядь, святое!

Для старых-добрых Function-Based Views (FBV)

Тут всё просто, как три копейки — навешиваешь декоратор на функцию, и поехали.

from django.db import transaction
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status

@api_view(['POST'])
@transaction.atomic
def create_order(request):
    try:
        # Допустим, создаём заказ и тут же списываем товар со склада
        order = create_order_in_db(request.data)
        update_stock_count(order.product, order.quantity)

        # Если добрались сюда — значит, всё гуд, транзакция закоммитится
        return Response({'status': 'success'}, status=status.HTTP_201_CREATED)

    except Exception as e:
        # А если где-то вылезла ошибка — всё автоматически откатится, как будто ты нихуя и не делал
        return Response({'error': str(e)}, status=status.HTTP_400_BAD_REQUEST)

Для модных Class-Based Views (CBV)

Тут уже чуть хитрожопей, потому что декоратор на метод класса так просто не нацепишь. Нужен специальный посредник — method_decorator.

from django.db import transaction
from django.utils.decorators import method_decorator
from rest_framework.views import APIView
from rest_framework.response import Response

class OrderAPIView(APIView):
    @method_decorator(transaction.atomic)
    def post(self, request, *args, **kwargs):
        # Вся твоя бизнес-логика, которая должна быть атомарной, пишется тут
        # ...
        return Response({'status': 'success'})

А если ты такой ленивый, что хочешь, чтобы ВСЕ методы в классе (GET, POST, PUT, DELETE) работали в транзакции, то можно прицепить декоратор к методу dispatch — это типа главный распределитель запросов в классе. Пишешь так: @method_decorator(transaction.atomic, name='dispatch') над всем классом, и дело в шляпе. Но осторожно, это как давать всем подряд ключи от своей квартиры — иногда излишне.

В общем, суть в том, чтобы твои данные не превратились в кашу, если что-то пойдёт не так. А transaction.atomic — это твой верный пёс, который следит, чтобы в базе был порядок, а не пиздец.