Ответ
Декоратор @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 не перепутай, а то получится ебанина: будешь пароль всей базе пользователей разом менять, охуеешь потом.