Custom Actions in Django REST Framework ViewSets with @action
Last updated 2 weeks, 6 days ago | 95 views 75 5

Django REST Framework’s ViewSet
classes handle the standard CRUD operations well. But what if you need to implement custom logic—like publishing a blog post, marking an item as featured, or resetting a password?
That's where custom actions using the @action
decorator come in.
This article covers:
-
What the
@action
decorator is -
How to add custom routes to a
ViewSet
-
GET vs POST custom actions
-
Full example with code
-
Tips and common pitfalls
What is the @action
Decorator?
The @action
decorator (from rest_framework.decorators
) allows you to define custom routes inside a ViewSet that aren't tied to the standard CRUD operations (list
, retrieve
, create
, etc.).
You can create extra routes like:
GET /books/<pk>/publish/
POST /users/<pk>/reset_password/
When to Use Custom Actions
Use the @action
decorator when:
-
You need an endpoint that performs a specific operation on a resource
-
It doesn’t fit the standard CRUD structure
-
You want to keep the functionality grouped within the ViewSet
Step-by-Step Example: Add a publish
Action to a Book
1. Model
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=100)
is_published = models.BooleanField(default=False)
2. Serializer
# serializers.py
from rest_framework import serializers
from .models import Book
class BookSerializer(serializers.ModelSerializer):
class Meta:
model = Book
fields = '__all__'
3. ViewSet with Custom Action
# views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework import status
from .models import Book
from .serializers import BookSerializer
class BookViewSet(ModelViewSet):
queryset = Book.objects.all()
serializer_class = BookSerializer
@action(detail=True, methods=['post'])
def publish(self, request, pk=None):
book = self.get_object()
book.is_published = True
book.save()
return Response({'status': 'book published'})
✅ This creates a new endpoint: POST /books/<pk>/publish/
4. Add to urls.py
via Router
# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import BookViewSet
router = DefaultRouter()
router.register(r'books', BookViewSet)
urlpatterns = [
path('api/', include(router.urls)),
]
GET vs POST Actions
-
Use
methods=['get']
for safe, read-only actions -
Use
methods=['post']
for modifying state
Example GET Action
@action(detail=True, methods=['get'])
def summary(self, request, pk=None):
book = self.get_object()
return Response({'title': book.title, 'author': book.author})
Access it via:
GET /books/3/summary/
Permissions on Custom Actions
You can apply permissions per action:
@action(detail=True, methods=['post'], permission_classes=[IsAdminUser])
def publish(self, request, pk=None):
...
Summary
Feature | Description |
---|---|
@action |
Adds custom method-based endpoints to a ViewSet |
detail=True |
Applies to individual instance (/books/<pk>/... ) |
detail=False |
Applies to the entire collection (/books/custom/ ) |
methods=['post'] |
Use for actions that change state |
methods=['get'] |
Use for read-only info |
⚠️ Common Pitfalls
Problem | Fix |
---|---|
URL not found | Make sure the action is correctly decorated with @action |
detail=True vs False confusion |
Use detail=True for instance actions; False for collection actions |
Wrong HTTP method | Match methods=['get'] or ['post'] to how you call the endpoint |
Permissions not enforced | Use permission_classes=[] on the custom action |
Best Practices
-
✅ Keep your custom action names RESTful (
publish
,approve
,reset_password
) -
✅ Document the custom routes clearly for front-end teams
-
✅ Use
detail=False
for collection-level actions (e.g.,/books/archive_all/
) -
✅ Don’t abuse
@action
to replace good model/view logic
✅ What’s Next?
You can explore:
-
@action(detail=False)
for collection-level actions -
Using
throttle_classes
for rate-limiting custom actions -
Testing custom actions with
APIClient
in DRF tests