Django REST Framework
🔌 Django REST Framework
JsonResponse straight out of a view — fine for one endpoint, repetitive across a whole CRUD API. Django REST Framework (DRF) is Django's answer, the same way FastAPI itself is really "Starlette plus Pydantic bundled together": DRF pairs serializers (validation in, JSON out) with viewsets and routers that generate an entire CRUD API from a handful of lines.
Installing DRF
Like every app, DRF does nothing until it's registered here — the same INSTALLED_APPS discipline from Chapter 1 of the last course.
📦 Serializers: Django's Pydantic Equivalent
A ModelSerializer plays exactly the role a Pydantic model plays in FastAPI — it validates incoming data and converts model instances to JSON-safe data, generated from the model the same way ModelForm generated a form:
FastAPI's Pydantic Model vs DRF's Serializer
FastAPI: Pydantic Model
Not tied to a database model — often paired with a separate SQLAlchemy model for the actual table.
DRF: ModelSerializer
Generated directly from the Django model — one less shape to keep manually in sync.
API Views
The simplest DRF view is a function decorated with @api_view — like @login_required, it's a decorator adding framework behavior on top of an ordinary function:
🧰 ViewSets & Routers
Writing a separate view for list/create/retrieve/update/destroy is exactly the repetition DRF exists to remove. A ModelViewSet implements all five from one class; a Router generates the matching urls.py entries automatically:
Same Idea as Rails' resources
router.register("books", BookViewSet) generating 5 routes from one line is directly analogous to Rails' resources :books generating 7 — Chapter 2's hand-written urlpatterns list was the "before" picture this replaces.
queryset + serializer_class
The only two things a ModelViewSet needs to know — which rows, and how to shape them — everything else (routing to the right action, calling is_valid(), calling save()) is handled for you.
💻 Coding Challenges
Challenge 1: Write a Serializer
Write an AuthorSerializer(serializers.ModelSerializer) for the Author model, exposing id, name, and bio (but not birth_year), and show the Python code to serialize a single Author instance to a dict.
Goal: Practice writing a ModelSerializer with an explicit field allowlist.
Challenge 2: Build a Full CRUD API
Using the AuthorSerializer from Challenge 1, write an AuthorViewSet(viewsets.ModelViewSet) and the router registration that exposes it at /authors/.
Goal: Practice the viewset + router pattern that replaces five hand-written views and five hand-written URL patterns.
Challenge 3: Validate Incoming Data
Using a serializer directly (not through a view), demonstrate what serializer.is_valid() and serializer.errors return for a submitted price value that's a negative number, assuming price uses DRF's DecimalField with a min_value validator.
Goal: Practice reasoning about serializer validation independent of any view or HTTP request.
fields = "__all__" on a Serializer Has the Same Risk as a FormThe mass-assignment danger from the last course's Forms chapter applies identically to ModelSerializer: fields = "__all__" exposes every model field — including ones like is_staff on a User-adjacent model — to both reading AND writing through the API. An API is, if anything, a more likely target than an HTML form, since it's trivial to script arbitrary JSON bodies against it. Use an explicit fields list on every serializer that touches anything sensitive, exactly as this course has insisted for ModelForm.
🎯 What's Next
With a real API in place, the next chapter covers proving it actually works: Testing Django Apps — TestCase, the Django test client, and fixtures for setting up predictable test data.