Topics in this subject
Django 4 min read Updated 11 Aug 2026

DRF Views and Routers

Building APIs rapidly using APIView, Generic Views, ViewSets, and automatic routing.

🧑‍🏫 Sabse pehle — simple mein samjho#

Jaise normal Django mein FBV aur CBV the, DRF mein bhi view likhne ke alag-alag tareeqe hain. Sabse basic hai @api_view, jo normal function jaisa hai. Phir aate hain Generic Views, jahan DRF tumhe bolta hai "Bhai, agar tumhe sirf ek list return karni hai, toh baar-baar code mat likho, bas mera class use karlo". Aur sabse upar aata hai ViewSet — ye ek hi class ke andar Create, Read, Update, aur Delete sab sambhal leta hai! Sath hi, ViewSets ke liye humein manual URLs nahi likhne padte, Router apne aap URLs generate kar deta hai.

1. Function-Based Views (@api_view)#

This is the most explicit way to write an API endpoint. You define exactly what HTTP methods are allowed, grab the data, serialize it, and return a Response.

from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Post
from .serializers import PostSerializer

@api_view(['GET', 'POST'])
def post_list_create(request):
    if request.method == 'GET':
        posts = Post.objects.all()
        # 'many=True' is crucial when serializing a QuerySet (a list of objects)
        serializer = PostSerializer(posts, many=True)
        return Response(serializer.data)
        
    elif request.method == 'POST':
        serializer = PostSerializer(data=request.data)
        if serializer.is_valid():
            # In DRF, you often pass extra data (like the logged-in user) directly into save()
            serializer.save(author=request.user)
            # Return HTTP 201 Created on success
            return Response(serializer.data, status=201)
        # Automatically returns a dictionary of validation errors with a 400 Bad Request status
        return Response(serializer.errors, status=400)

(In urls.py: path('posts/', views.post_list_create))

2. Generic Views (The Shortcut)#

Writing the same GET/POST logic over and over gets tedious. DRF provides Generic Views that abstract this away. You only need to provide two things: a queryset and a serializer_class.

from rest_framework import generics
from .models import Post
from .serializers import PostSerializer

# ListCreateAPIView handles both GET (list all) and POST (create new) automatically!
class PostListCreateView(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    
    # Optional: Override a specific behavior, like forcing the author to be the current user
    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

# RetrieveUpdateDestroyAPIView handles GET (single item), PUT (update), and DELETE automatically!
class PostDetailView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

(In urls.py: path('posts/', views.PostListCreateView.as_view()) and path('posts/<int:pk>/', views.PostDetailView.as_view()))

3. ViewSets and Routers (The Ultimate Shortcut)#

Wait! Even with Generic views, we still had to write two separate classes (one for the list, one for the detail) and two separate URLs. What if we combined them into one massive controller? That's a ModelViewSet.

from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer

# This SINGLE class automatically provides:
# - GET /posts/ (List all)
# - POST /posts/ (Create new)
# - GET /posts/1/ (Retrieve one)
# - PUT /posts/1/ (Update one)
# - DELETE /posts/1/ (Delete one)
class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

The Router (urls.py)#

Because a ViewSet handles multiple URLs (with and without the <int:pk>), you cannot use a standard path(). You use a Router.

# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import PostViewSet

# Initialize the router
router = DefaultRouter()

# Register our ViewSet. 
# This automatically generates all 5 endpoint URLs under the 'posts/' prefix!
router.register(r'posts', PostViewSet, basename='post')

urlpatterns = [
    # Include all the generated URLs in our app's routing
    path('', include(router.urls)),
]

When to use which?#

  • Use @api_view for highly custom actions (like processing a payment or calling an external API) that don't map cleanly to a database model.
  • Use Generic Views when you only want to expose specific actions (e.g., only Read and Create, but no Delete).
  • Use ViewSets for full standard CRUD applications to save massive amounts of time.