Views: Function-Based and Class-Based
Introduction
Views are callables that take an HttpRequest and return an HttpResponse. Django supports function-based views (FBV) for straightforward logic and class-based views (CBV) for reusable patterns like list/detail and CRUD. This chapter implements handlers for the routes from URLs and Routing, covers request/response objects, and introduces generic class views you will use in the blog project.
Prerequisites
- URLs and Routing — named routes and path converters
blog/urls.pywithpost_list,post_detail, etc. (stubs OK)
Function-Based Views (FBV)
Minimal pattern
from django.http import HttpResponse
def home(request):
return HttpResponse("Hello Django")Every view receives request as the first argument. URL-captured kwargs follow:
def post_detail(request, pk):
return HttpResponse(f"Post id: {pk}")HttpRequest essentials
| Attribute | Use |
|---|---|
request.method | "GET", "POST", "PUT", … |
request.GET | Query dict (request.GET.get("page")) |
request.POST | Form body (POST); not for JSON—use request.body |
request.headers | Header access (Django 2.2+) |
request.body | Raw bytes (JSON APIs) |
request.user | Current user (after auth middleware) |
request.path | Path without query string |
Example:
def post_list(request):
page = request.GET.get("page", "1")
if request.method == "GET":
return HttpResponse(f"Listing page {page}")
return HttpResponse("Method not allowed", status=405)JsonResponse
For APIs (before DRF):
from django.http import JsonResponse
def api_health(request):
return JsonResponse({"status": "ok", "version": "1.0"})HttpResponse options
from django.http import HttpResponse
def custom(request):
response = HttpResponse("Created", status=201)
response["X-Custom-Header"] = "value"
response.set_cookie("visited", "yes", max_age=3600)
return responseHTTP method restrictions
from django.views.decorators.http import require_GET, require_http_methods
@require_GET
def post_list(request):
...
@require_http_methods(["GET", "POST"])
def post_create(request):
if request.method == "GET":
return HttpResponse("Show form")
return HttpResponse("Process form", status=201)Prefer these decorators over manual if request.method when rules are simple.
redirect and get_object_or_404
from django.shortcuts import get_object_or_404, redirect
def post_delete(request, pk):
# post = get_object_or_404(Post, pk=pk) # after models exist
# post.delete()
return redirect("post-list")get_object_or_404 raises 404 when the row is missing—better UX than bare .get() exceptions.
Class-Based Views (CBV)
View base class
from django.http import HttpResponse
from django.views import View
class PostListView(View):
def get(self, request):
return HttpResponse("GET post list")
def post(self, request):
return HttpResponse("POST create post", status=201)Wire in urls.py:
path("posts/", views.PostListView.as_view(), name="post-list"),Code explanation:
- Django calls
as_view()once at URL load; it returns a function WSGI can invoke - Method name maps to HTTP verb:
get,post,put,delete
Generic display views
With a Post model (defined in chapter 09):
URLs:
path("posts/", views.PostListView.as_view(), name="post-list"),
path("posts/<int:pk>/", views.PostDetailView.as_view(), name="post-detail"),ListView / DetailView query the model, build context, and render templates—minimal code for standard read pages.
Generic editing views
reverse_lazy delays URL resolution until the class is used—required in class body; use reverse() inside methods.
Forms and validation integrate here—details in Forms and CSRF.
FBV vs CBV: When to Use Which
| Choose FBV | Choose CBV |
|---|---|
| One-off logic, webhooks, custom JSON | Standard list/detail/create/update/delete |
| Heavy branching or non-HTTP protocols | Same structure across many models |
| You want maximum explicitness | Leverage LoginRequiredMixin, permissions |
Many codebases mix both in the same app.
Request/Response Lifecycle (View Slice)
Middleware → URL resolver → view(request, **kwargs)
↓
HttpResponse (status, headers, body)
↓
Middleware (reverse) → clientrequest.user is populated by AuthenticationMiddleware—anonymous users get AnonymousUser.
Parsing JSON in FBV (API preview)
DRF replaces much of this boilerplate—chapter 19.
LoginRequiredMixin (preview)
from django.contrib.auth.mixins import LoginRequiredMixin
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ["title", "body"]
login_url = "/accounts/login/"FBV equivalent: @login_required decorator—Authentication chapter.
Example: blog/views.py (combined)
Start with FBV while learning; refactor hot paths to CBV when patterns repeat.
Common Mistakes
| Mistake | Result | Fix |
|---|---|---|
Forgot @require_POST on delete | CSRF / accidental GET delete | Method decorators or POST forms |
CBV without .as_view() | TypeError at request time | PostListView.as_view() in urls |
reverse_lazy vs reverse in class attrs | Import-time NoReverseMatch | reverse_lazy in class body |
Reading request.POST on JSON API | Empty dict | Parse request.body |
| 500 instead of 404 | .get() on missing row | get_object_or_404 |
Post-Chapter Checklist
- You can write FBV with
request.GET,request.method - You use
JsonResponse,redirect, method decorators - You wire
View.as_view()and genericListView/DetailView - You know when FBV vs CBV is appropriate
- Routes from chapter 06 have working view callables
FAQ
request.POST vs request.data?
request.data is DRF-only. Plain Django: POST for forms, body for JSON.
Class-based vs function-based performance?
Negligible difference. Choose for structure and maintainability.
One view, multiple URLs?
Use different path() entries pointing to the same view with different kwargs, or CBV with pk optional.
Async views?
Django supports async def views with ASGI—advanced; this track uses sync WSGI patterns first.
HttpResponse vs render()?
render(request, template, context) combines template loading and HttpResponse—next template chapter.