Difficulty: Advanced
DRF provides several view classes at different abstraction levels. APIView is the base class that adds DRF features (authentication, permissions, content negotiation) to Django's View. GenericAPIView adds queryset and serializer handling. Concrete view classes (ListAPIView, RetrieveAPIView, etc.) add specific behavior. ViewSets combine multiple view actions into a single class.
APIView is the DRF equivalent of Django's View. You define get(), post(), put(), patch(), delete() methods. It provides request parsing (request.data instead of request.POST), response rendering (Response instead of HttpResponse), authentication, permission checking, and exception handling.
GenericAPIView extends APIView with queryset and serializer_class attributes. Combined with mixins (ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin), it provides standard CRUD operations. Concrete generic views combine GenericAPIView with specific mixins.
ViewSets are the highest abstraction level. Instead of defining separate views for list, create, retrieve, update, and delete, a ViewSet handles all of them in a single class. ModelViewSet provides complete CRUD functionality with minimal code - just set queryset and serializer_class.
ViewSets use 'actions' instead of HTTP method handlers: list() (GET /), create() (POST /), retrieve() (GET /{pk}/), update() (PUT /{pk}/), partial_update() (PATCH /{pk}/), destroy() (DELETE /{pk}/). The Router (covered in the next lesson) maps these actions to URL patterns automatically.
You can restrict which actions a ViewSet supports using mixins. Instead of ModelViewSet (which includes all CRUD), you can mix specific mixins: CreateModelMixin + ListModelMixin + GenericViewSet gives you only create and list operations.
Customizing ViewSet behavior follows the same patterns as generic views: override get_queryset(), get_serializer_class(), perform_create(), perform_update(), and perform_destroy() for custom logic.
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Article
from .serializers import ArticleSerializer
class ArticleListView(APIView):
def get(self, request):
articles = Article.objects.all()
serializer = ArticleSerializer(articles, many=True)
return Response(serializer.data)
def post(self, request):
serializer = ArticleSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(author=request.user)
return Response(serializer.data, status=status.HTTP_201_CREATED)
class ArticleDetailView(APIView):
def get(self, request, pk):
article = get_object_or_404(Article, pk=pk)
serializer = ArticleSerializer(article)
return Response(serializer.data)
def put(self, request, pk):
article = get_object_or_404(Article, pk=pk)
serializer = ArticleSerializer(article, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
def delete(self, request, pk):
article = get_object_or_404(Article, pk=pk)
article.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
APIView gives full control over each HTTP method. request.data handles JSON, form data, and multipart. Response auto-negotiates content type. raise_exception=True auto-returns 400 on validation errors.
from rest_framework import generics
from .models import Article
from .serializers import ArticleSerializer
# List + Create
class ArticleListCreateView(generics.ListCreateAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
def perform_create(self, serializer):
serializer.save(author=self.request.user)
# Retrieve + Update + Delete
class ArticleDetailView(generics.RetrieveUpdateDestroyAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
# Read-only views
class ArticleListView(generics.ListAPIView):
queryset = Article.objects.filter(is_published=True)
serializer_class = ArticleSerializer
class ArticleRetrieveView(generics.RetrieveAPIView):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
lookup_field = 'slug' # Use slug instead of pk
Generic views provide pre-built combinations: ListCreateAPIView (list + create), RetrieveUpdateDestroyAPIView (detail + edit + delete). perform_create() is the hook for setting non-form fields. lookup_field customizes the URL parameter.
from rest_framework import viewsets, permissions
from .models import Article
from .serializers import ArticleSerializer, ArticleDetailSerializer
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
permission_classes = [permissions.IsAuthenticatedOrReadOnly]
def get_serializer_class(self):
if self.action == 'retrieve':
return ArticleDetailSerializer
return ArticleSerializer
def get_queryset(self):
qs = super().get_queryset()
if self.action == 'list':
return qs.filter(is_published=True)
return qs
def perform_create(self, serializer):
serializer.save(author=self.request.user)
def perform_update(self, serializer):
serializer.save(edited_by=self.request.user)
ModelViewSet provides all CRUD actions in a single class. Override get_serializer_class() for action-specific serializers, get_queryset() for action-specific filtering, and perform_create/update for custom save logic.
from rest_framework import viewsets, mixins
from .models import Tag
from .serializers import TagSerializer
# Read-only ViewSet (list + retrieve only)
class TagViewSet(
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet,
):
queryset = Tag.objects.all()
serializer_class = TagSerializer
# Create + List ViewSet (no update or delete)
class CommentViewSet(
mixins.CreateModelMixin,
mixins.ListModelMixin,
viewsets.GenericViewSet,
):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
def perform_create(self, serializer):
serializer.save(author=self.request.user)
# Or use ReadOnlyModelViewSet
class CategoryViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Category.objects.all()
serializer_class = CategorySerializer
Mix specific mixins with GenericViewSet to control which actions are available. ReadOnlyModelViewSet provides list + retrieve. This gives fine-grained control over API capabilities.
APIView, GenericAPIView, ViewSet, ModelViewSet, Mixins