DRF: Views & ViewSets

A serializer knows how to shape data, but nothing yet decides what happens when a request actually arrives at a URL. That's the job of a DRF view — and DRF gives you a spectrum of options here, from full manual control at one end to an almost automatic shortcut at the other.

APIView: The REST Equivalent of a Plain CBV

APIView is DRF's own base class, built on the same idea as the class-based views from Class-Based Views — one method per HTTP verb — but aimed at APIs specifically. Instead of returning a template with render(), an APIView method returns a DRF Response object built from serialized data:

# taskflow/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Task
from .serializers import TaskSerializer

class TaskListAPIView(APIView):
    def get(self, request):
        tasks = Task.objects.all()
        serializer = TaskSerializer(tasks, many=True)
        return Response(serializer.data)

    def post(self, request):
        serializer = TaskSerializer(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)

get() handles listing every task — note many=True, which tells the serializer it's handling a queryset of several objects rather than one. post() handles creating a new task, following the exact same validate-then-save pattern from the previous lesson. Every response detail — what gets queried, what status code comes back, what the error response looks like — is written out explicitly, which is precisely the appeal of APIView: full control, at the cost of writing that control out by hand.

ViewSet: Bundling list/create/retrieve/update/delete Into One Class

A huge share of real API endpoints follow the exact same shape: list all objects, create one, retrieve one, update one, delete one — the same five operations, over and over, for model after model. Writing a separate APIView for each of those five actions, for every model, gets repetitive fast. A ModelViewSet collapses all five into a single class:

# taskflow/views.py
from rest_framework import viewsets
from .models import Task
from .serializers import TaskSerializer

class TaskViewSet(viewsets.ModelViewSet):
    queryset = Task.objects.all()
    serializer_class = TaskSerializer

That's genuinely the whole class — two lines of actual configuration — and it already supports listing, creating, retrieving one task, updating one, and deleting one. Compare that to the TaskListAPIView above, which only covered two of those five actions in roughly a dozen lines; a ModelViewSet covering all five with equivalent hand-written APIView code would run several times longer.

Wiring a ViewSet's URLs With a Router

A ViewSet doesn't map onto a single URL the way a normal view does — it needs several related URL patterns (the list endpoint, the detail endpoint, and so on). DRF's router generates all of them automatically from one registration line:

# taskflow/urls.py
from rest_framework.routers import DefaultRouter
from .views import TaskViewSet

router = DefaultRouter()
router.register('tasks', TaskViewSet)

urlpatterns = router.urls

That single router.register('tasks', TaskViewSet) call produces the standard set of REST URL patterns on its own — /tasks/ for listing and creating, and /tasks/<pk>/ for retrieving, updating, and deleting one task — without writing out a single path() call by hand. It's the same kind of convenience win ModelViewSet offers for view logic, applied to URL configuration instead.

APIView vs ModelViewSet: An Honest Comparison

APIViewModelViewSet
Control over each endpointFull — every line of behavior is explicitLimited by default — you're accepting the standard CRUD behavior
Amount of code for standard CRUDA lot — one method per action, repeated per modelMinimal — two or three attribute lines typically covers it
URL wiringManual path() entries, one per actionAutomatic, via a router
Best fitEndpoints with genuinely custom logic that doesn't map onto plain CRUDStraightforward CRUD over a single model, which covers most API endpoints in practice

Generic API Views: A Middle Ground

Between those two extremes sit DRF's generic views — classes like ListAPIView and RetrieveAPIView, which handle exactly one action each (just listing, or just retrieving one object) rather than all five. They're worth knowing exist: reach for one when an endpoint should support just reading a list or just reading a single object, and nothing else — a read-only reporting endpoint is a typical example — without either writing a full APIView by hand or pulling in every action a ModelViewSet provides.

Note: All of the view and router code above is correct, real DRF code — but this website's own project doesn't run a live Django server, so none of these endpoints are actually reachable from this page.
Next up: Right now, TaskViewSet is wide open — anyone can create, edit, or delete any task. DRF: Authentication & Permissions covers locking that down properly.