Для чего используется декоратор @action в Django REST Framework

Ответ

Декоратор @action в Django REST Framework (DRF) используется для добавления пользовательских маршрутов (эндпоинтов) к стандартному набору ViewSet.

Стандартный ViewSet предоставляет CRUD-операции (list, create, retrieve, update, destroy). Если требуется дополнительная логика, не вписывающаяся в CRUD, на помощь приходит @action.

Ключевой параметр — detail:

  • detail=True: Действие применяется к конкретному объекту. URL будет содержать pk объекта. Например, POST /users/1/set_password/.
  • detail=False: Действие применяется ко всему набору объектов (коллекции). URL не будет содержать pk. Например, GET /users/active_users/.

Пример:

Добавим эндпоинт для смены пароля конкретного пользователя и эндпоинт для получения всех активных пользователей.

from django.contrib.auth.models import User
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .serializers import UserSerializer

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    # Действие для конкретного объекта (пользователя)
    # URL: /users/{pk}/set_password/
    @action(detail=True, methods=['post'])
    def set_password(self, request, pk=None):
        user = self.get_object()
        password = request.data.get('password')
        if not password:
            return Response({'error': 'Password not provided'}, status=status.HTTP_400_BAD_REQUEST)

        user.set_password(password)
        user.save()
        return Response({'status': 'password set'})

    # Действие для всей коллекции
    # URL: /users/active_users/
    @action(detail=False, methods=['get'])
    def active_users(self, request):
        active_users = User.objects.filter(is_active=True)
        serializer = self.get_serializer(active_users, many=True)
        return Response(serializer.data)

Основные параметры @action:

  • detail (bool): Определяет, привязан ли маршрут к объекту или коллекции.
  • methods (list): Список разрешенных HTTP-методов (e.g., ['get', 'post']).
  • url_path (str): Позволяет задать кастомный сегмент URL вместо имени функции.
  • url_name (str): Имя для реверсирования URL (reverse()).

Ответ 18+ 🔞

О, смотри-ка, про декоратор @action в DRF. Ну, это ж классика, блядь! Сидишь ты такой, пишешь свой ViewSet, всё красиво: list, create, retrieve — стандартный CRUD-набор, как у всех. А потом бац — и тебе надо какую-то свою, ебучую логику прикрутить, которая в эти рамки не лезет. Вот тут-то наш герой и появляется, ёпта!

Представь, у тебя есть юзеры. Ну, создавать, получать, обновлять — это всё есть. А ты вдруг решил: «А дай-ка я им пароль менять через API позволю!» Или: «А покажи-ка мне только активных!» Вот на это стандартных методов-то и нету. И тут ты такой: «Муму!» — то есть, @action, блядь.

Самый главный параметр, на котором всё ебётся — это detail. Запомни раз и навсегда, чтобы потом не метаться, как Герасим с совестью:

  • detail=True — это когда ты прицепился к конкретному объекту, к этой самой единичной сущности. В URL у тебя будет его pk торчать. Типа POST /users/1/set_password/. «Эй, пользователь с айдишником 1, иди сюда, сука, пароль менять!»
  • detail=False — это когда ты работаешь со всей кучей сразу, с коллекцией. Без привязки к кому-то одному. URL будет простой: GET /users/active_users/. «Ну-ка, все активные, блядь, построились!»

Вот, смотри, как это в коде выглядит, чтобы не быть, прости господи, распиздяем:

from django.contrib.auth.models import User
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .serializers import UserSerializer

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    # Это действие для одного юзера. Привязано, блядь, к нему лично.
    # URL будет: /users/{pk}/set_password/
    @action(detail=True, methods=['post'])
    def set_password(self, request, pk=None):
        user = self.get_object() # Вот он, наш бедолага, которого нашли по pk
        password = request.data.get('password')
        if not password:
            return Response({'error': 'Password not provided'}, status=status.HTTP_400_BAD_REQUEST)

        user.set_password(password)
        user.save()
        return Response({'status': 'password set'})

    # А это — для всей толпы. Коллективное действие, блядь.
    # URL: /users/active_users/
    @action(detail=False, methods=['get'])
    def active_users(self, request):
        active_users = User.objects.filter(is_active=True) # Все, кто не спит
        serializer = self.get_serializer(active_users, many=True)
        return Response(serializer.data)

Ну и по мелочи, там ещё параметры есть у этого декоратора, не хуй собачий:

  • methods — ну тут ясно, какие HTTP-методы разрешены. ['get'], ['post'] или ['get', 'post'], если ты такой весь из себя универсальный.
  • url_path — если имя функции active_users тебе в URL не нравится, можешь своё придумать. Типа url_path='zombies'.
  • url_name — это для реверса URL, когда через reverse() вызываешь. Чтобы не путаться, как пьяный в луже.

Вот и вся магия, блядь. Ничего сложного, но без этого — ни туды и ни сюды. Главное — detail не перепутай, а то получится ебанина: будешь пароль всей базе пользователей разом менять, охуеешь потом.