Django: How to Write Your Own Custom ViewSet Class (With Examples)
Last updated 4 months ago | 310 views 75 5

Introduction: Why Write Your Own ViewSet?
Django Rest Framework (DRF) provides powerful generic ViewSet
classes like ModelViewSet
and ReadOnlyModelViewSet
. These work great for CRUD operations out-of-the-box, but what if you need fine-grained control over your views?
Creating your own custom ViewSet lets you:
-
Define precise behavior for each HTTP method
-
Add custom authentication, throttling, or permissions
-
Avoid bloated logic in generic views
-
Improve performance by customizing database access
This article walks you through how to build your own ViewSet from scratch, with practical examples and tips.
Understanding the Basics of a ViewSet
A ViewSet
in DRF is a class that combines logic for a set of related views. Instead of writing separate APIView
classes for each action (like list, create, retrieve), a ViewSet
groups them together.
Here's what Django Rest Framework provides:
Built-in ViewSet | Inherits From | Provides |
---|---|---|
ViewSet |
APIView |
No default actions |
GenericViewSet |
ViewSet + mixins |
Basic behavior for CRUD |
ModelViewSet |
GenericViewSet + CRUD mixins |
Full CRUD out-of-the-box |
When you write your own ViewSet, you typically subclass ViewSet
or GenericViewSet
and implement the methods manually.
Step-by-Step: Writing a Custom ViewSet Class
1. Create a Serializer
First, define a serializer for your model:
# serializers.py
from rest_framework import serializers
from .models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = ['id', 'title', 'content', 'created_at']
2. Write a Custom ViewSet Class
Let’s build a CustomArticleViewSet
that supports list, retrieve, and create operations.
# views.py
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from rest_framework import status
from .models import Article
from .serializers import ArticleSerializer
from django.shortcuts import get_object_or_404
class CustomArticleViewSet(ViewSet):
"""
A custom ViewSet for listing, retrieving, and creating articles.
"""
def list(self, request):
# GET /articles/
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
# GET /articles/<pk>/
article = get_object_or_404(Article, pk=pk)
serializer = ArticleSerializer(article)
return Response(serializer.data)
def create(self, request):
# POST /articles/
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
3. Register the ViewSet with a Router
# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import CustomArticleViewSet
router = DefaultRouter()
router.register(r'articles', CustomArticleViewSet, basename='article')
urlpatterns = [
path('', include(router.urls)),
]
✅ Full Working Example
Let’s put everything together.
models.py
from django.db import models
class Article(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
serializers.py
from rest_framework import serializers
from .models import Article
class ArticleSerializer(serializers.ModelSerializer):
class Meta:
model = Article
fields = '__all__'
views.py
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response
from rest_framework import status
from .models import Article
from .serializers import ArticleSerializer
from django.shortcuts import get_object_or_404
class CustomArticleViewSet(ViewSet):
def list(self, request):
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
def retrieve(self, request, pk=None):
article = get_object_or_404(Article, pk=pk)
serializer = ArticleSerializer(article)
return Response(serializer.data)
def create(self, request):
serializer = ArticleSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import CustomArticleViewSet
router = DefaultRouter()
router.register(r'articles', CustomArticleViewSet, basename='article')
urlpatterns = [
path('', include(router.urls)),
]
Tips & Common Pitfalls
✅ Best Practices
-
Always use
get_object_or_404()
for safety inretrieve
,update
, anddestroy
. -
Keep each method short and readable.
-
Use
basename
when registering a customViewSet
to avoid router errors. -
Override only the methods you need (e.g.,
update
,partial_update
,destroy
).
⚠️ Common Pitfalls
-
Forgetting
basename
in router registration whenqueryset
is not defined. -
Mixing concerns — don't put business logic inside the ViewSet; keep it clean.
-
Missing serializer validation before saving.
-
Skipping authentication/permissions — always secure your endpoints!
When Should You Write a Custom ViewSet?
Use Case | Recommended Approach |
---|---|
Standard CRUD (Create/Read/Update/Delete) | Use ModelViewSet |
Custom logic in some actions | Subclass GenericViewSet + override specific methods |
Fully custom behavior or non-model views | Use ViewSet from scratch |
Summary and Best Practices
Creating your own custom ViewSet in Django gives you full control over how each HTTP method behaves. It's ideal when:
-
You need custom business logic per action.
-
You want lightweight views without generic mixins.
-
You aim to build clean, maintainable APIs.
Start with
ModelViewSet
when possible, but don’t be afraid to roll up your sleeves and write your own ViewSet when customization calls for it.
Final Thoughts
Custom ViewSets are a powerful tool in your Django REST toolbox. Once you master them, you'll be able to build APIs that are not just functional, but tailored, secure, and elegant.
Ready to build smarter APIs? Start writing your own ViewSet today.