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.py with post_list, post_detail, etc. (stubs OK)

Function-Based Views (FBV)

Minimal pattern

python
from django.http import HttpResponse
 
def home(request):
    return HttpResponse("Hello Django")

Every view receives request as the first argument. URL-captured kwargs follow:

python
def post_detail(request, pk):
    return HttpResponse(f"Post id: {pk}")

HttpRequest essentials

AttributeUse
request.method"GET", "POST", "PUT", …
request.GETQuery dict (request.GET.get("page"))
request.POSTForm body (POST); not for JSON—use request.body
request.headersHeader access (Django 2.2+)
request.bodyRaw bytes (JSON APIs)
request.userCurrent user (after auth middleware)
request.pathPath without query string

Example:

python
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):

python
from django.http import JsonResponse
 
def api_health(request):
    return JsonResponse({"status": "ok", "version": "1.0"})

HttpResponse options

python
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 response

HTTP method restrictions

python
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

python
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

python
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:

python
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:

python
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 FBVChoose CBV
One-off logic, webhooks, custom JSONStandard list/detail/create/update/delete
Heavy branching or non-HTTP protocolsSame structure across many models
You want maximum explicitnessLeverage LoginRequiredMixin, permissions

Many codebases mix both in the same app.

Request/Response Lifecycle (View Slice)

text
Middleware → URL resolver → view(request, **kwargs)

         HttpResponse (status, headers, body)

         Middleware (reverse) → client

request.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)

python
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

MistakeResultFix
Forgot @require_POST on deleteCSRF / accidental GET deleteMethod decorators or POST forms
CBV without .as_view()TypeError at request timePostListView.as_view() in urls
reverse_lazy vs reverse in class attrsImport-time NoReverseMatchreverse_lazy in class body
Reading request.POST on JSON APIEmpty dictParse request.body
500 instead of 404.get() on missing rowget_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 generic ListView / 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 HttpResponsenext template chapter.