Views
👁️ Views
urls.py hands a request to — the same role a FastAPI path operation function plays. The shape is different in two ways worth learning early: Django's request and response are their own classes rather than framework-injected parameters and auto-serialized return values, and Django gives you a choice between plain functions and classes for the view itself.
Function-Based Views (FBVs)
The simplest view is just a function that takes a request and returns a response:
Compare that to the FastAPI shape you already know:
FastAPI Path Operation vs Django FBV
FastAPI
Django
📨 The Request Object
Every view's first parameter is Django's HttpRequest — where FastAPI hands you individually typed parameters (path params, query params, a Pydantic body model), Django hands you one object and you pull what you need off it:
request.GET
A dict-like object of query string parameters — always present, empty on a request with no query string.
request.POST
Form-encoded POST body data — for JSON request bodies, you'd instead parse request.body yourself (Chapter 7 covers Forms properly).
request.method
One function often handles GET and POST for the same URL by branching on this — versus FastAPI's separate @app.get/@app.post decorators for the same path.
request.user
Once authentication middleware runs (Course 2), the logged-in user is attached here automatically — no manual dependency injection needed.
The Response Object
Every view must return something that is (or behaves like) an HttpResponse — Django provides several shortcuts for common cases:
🏗️ Class-Based Views (CBVs)
Django also lets a view be a class — instead of branching on request.method inside one function, each HTTP method gets its own class method:
Function-Based vs Class-Based, Same Route
Function-Based View
Class-Based View
Wiring a CBV into urls.py needs one extra step — .as_view() converts the class into something path() can call:
Dispatch by Method Name
View's base class reads request.method internally and calls the matching method (get, post, put, ...) — no manual if/elif chain needed.
Generic CBVs (Preview)
Django ships ready-made CBVs like ListView and DetailView that implement common patterns almost entirely for you — a deep dive is coming in Course 2.
💻 Coding Challenges
Challenge 1: Write a GET/POST Function-Based View
Write a subscribe(request) FBV that returns an HTML form on GET, and on POST, reads an email field from request.POST and returns a plain-text confirmation via HttpResponse.
Goal: Practice branching on request.method and reading from request.POST.
Challenge 2: Convert It to a Class-Based View
Rewrite the subscribe view from Challenge 1 as a SubscribeView(View) class with separate get() and post() methods, and update the corresponding urls.py entry to use .as_view().
Goal: Practice the FBV-to-CBV conversion and the .as_view() wiring step.
Challenge 3: Return the Right Response Type
Write three tiny views: one returning plain text, one returning JSON, and one returning a redirect to a named URL — using HttpResponse, JsonResponse, and redirect() respectively.
Goal: Build muscle memory for Django's explicit response types, instead of assuming a return value gets auto-converted the way FastAPI would.
HttpResponseIn FastAPI, returning a dict, a list, or even None from a path operation function is handled gracefully — FastAPI serializes whatever you give it. Django is stricter: a view must return an HttpResponse (or a subclass like JsonResponse, or the result of render()/redirect(), which are HttpResponse under the hood). A view that falls through without an explicit return — often because an if branch handles POST but there's no matching return for GET — raises a very literal ValueError: The view didn't return an HttpResponse object. It returned None instead.
🎯 What's Next
Views now return real responses — the next chapter covers where the HTML in render() actually comes from: Templates, the Django Template Language, template inheritance, and how its auto-escaping compares to what you've seen already.