← Назад к вопросам

Зачем нужен декоратор @action?

1.0 Junior🔥 81 комментариев
#Django#REST API и HTTP

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Декоратор @action в Django REST Framework

Декоратор @action используется для определения кастомных действий (эндпоинтов) в ViewSets, помимо стандартных CRUD операций (list, create, retrieve, update, destroy).

Основное назначение

@action нужен для:

  • Добавления custom endpoints: специфичные для бизнеса операции
  • Расширения ViewSet: без создания отдельного View
  • RESTful API: соблюдение REST принципов
  • Маршрутизации: автоматическое создание URL

Синтаксис

from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet

class ArticleViewSet(ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    @action(detail=True, methods=['post'])
    def publish(self, request, pk=None):
        article = self.get_object()
        article.is_published = True
        article.save()
        return Response({'status': 'article published'})

Параметры декоратора

detail=True vs detail=False:

Точка в том, что есть две категории действий:

  • detail=True применяется к конкретному объекту: /articles/{id}/publish/
  • detail=False применяется ко всей коллекции: /articles/top_products/
class ProductViewSet(ModelViewSet):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    
    @action(detail=True, methods=['post'])
    def add_to_cart(self, request, pk=None):
        product = self.get_object()
        return Response({'status': 'added'})
    
    @action(detail=False, methods=['get'])
    def top_products(self, request):
        top = Product.objects.filter(rating__gte=4.5)[:10]
        serializer = self.get_serializer(top, many=True)
        return Response(serializer.data)

Практические примеры

Публикация статей:

from rest_framework import status

class ArticleViewSet(ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    
    @action(detail=True, methods=['post'])
    def publish(self, request, pk=None):
        article = self.get_object()
        
        if not article.title or not article.content:
            return Response(
                {'error': 'Missing required fields'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        article.is_published = True
        article.published_at = timezone.now()
        article.save()
        
        serializer = self.get_serializer(article)
        return Response(serializer.data)

Лайк/Unlike:

class PostViewSet(ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    
    @action(detail=True, methods=['post'])
    def like(self, request, pk=None):
        post = self.get_object()
        user = request.user
        
        if post.likes.filter(id=user.id).exists():
            return Response(
                {'error': 'Already liked'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        post.likes.add(user)
        return Response({
            'status': 'liked',
            'likes_count': post.likes.count()
        })
    
    @action(detail=True, methods=['post'])
    def unlike(self, request, pk=None):
        post = self.get_object()
        post.likes.remove(request.user)
        return Response({
            'status': 'unliked',
            'likes_count': post.likes.count()
        })

URL маршрутизация

from django.urls import path, include
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register(r'articles', ArticleViewSet)

urlpatterns = [
    path('api/', include(router.urls)),
]

Автоматически созданные URLs:

  • GET /api/articles/ - list
  • POST /api/articles/ - create
  • GET /api/articles/{id}/ - retrieve
  • PUT /api/articles/{id}/ - update
  • DELETE /api/articles/{id}/ - destroy
  • POST /api/articles/{id}/publish/ - наш @action

Дополнительные параметры

@action(
    detail=True,
    methods=['post'],
    url_path='publish-article',
    url_name='article-publish',
    permission_classes=[IsAuthenticated],
)
def publish(self, request, pk=None):
    pass

Проверка прав доступа

from rest_framework.permissions import IsAuthenticated, IsAdminUser

@action(
    detail=True,
    methods=['post'],
    permission_classes=[IsAdminUser]
)
def approve(self, request, pk=None):
    # Только администраторы могут вызвать
    pass

Выводы

  • @action добавляет custom endpoints к ViewSet
  • detail=True: действие над конкретным объектом
  • detail=False: действие над коллекцией
  • methods: определяет HTTP методы (get, post, put, delete)
  • Автоматическая маршрутизация: URLs создаются роутером
  • RESTful: соблюдает REST принципы
  • Полезно для: бизнес-логики, не подходящей под стандартные CRUD операции