python 46 lines · 1 tab

Django REST Framework viewset actions

Priya Sharma Jan 2026
1 tab
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from blog.models import Post
from .serializers import PostSerializer


class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

    @action(detail=True, methods=['post'])
    def publish(self, request, pk=None):
        """Publish a post."""
        post = self.get_object()
        post.status = 'published'
        post.save()
        return Response({'status': 'post published'})

    @action(detail=True, methods=['post'])
    def unpublish(self, request, pk=None):
        """Unpublish a post."""
        post = self.get_object()
        post.status = 'draft'
        post.save()
        return Response({'status': 'post unpublished'})

    @action(detail=False, methods=['get'])
    def recent(self, request):
        """Get recent posts."""
        recent_posts = Post.objects.filter(
            status='published'
        ).order_by('-published_at')[:10]

        serializer = self.get_serializer(recent_posts, many=True)
        return Response(serializer.data)

    @action(detail=True, methods=['get'])
    def stats(self, request, pk=None):
        """Get post statistics."""
        post = self.get_object()
        return Response({
            'views': post.view_count,
            'comments': post.comments.count(),
            'likes': post.likes.count()
        })
1 file · python Explain with highlit

Custom actions extend viewsets beyond CRUD operations. I use @action decorator with detail=True/False for object-level or collection-level actions. This creates endpoints like /posts/1/publish/ or /posts/recent/. I specify HTTP methods, permissions, and serializers per action. Actions keep related logic together in one viewset. For complex operations not fitting REST patterns, actions provide flexibility. I use URL name from action for reverse(). This extends APIs naturally without new viewsets.


Related snips

Share this code

Here's the card — post it anywhere.

Django REST Framework viewset actions — share card
Link copied