skip to content

Class-Based Views

Django's View base class and the generic views built on it turn a request into method calls you override, from TemplateView to UpdateView. Interviewers ask when that inheritance helps.

part ofDjangooverview, primer and where to startread it →
on this pageshow

explore

questions

27

In a Django URLconf, why do you route to MyView.as_view() instead of the class itself, and what does as_view() return?

level: juniorimportance: must knowfreq 62%

answer

  1. path() wants a plain callable
  2. a closure over the class
  3. new object every request
  4. setup() then dispatch()

basics

~20 s

Django's path() needs a callable that takes a request and returns a response. MyView.as_view() returns such a function; on every request it builds a fresh MyView instance, calls setup(), then dispatch(), which runs the handler named after the HTTP method.

solid answer

~40 s

The URL resolver calls whatever you register as `view(request, *args, **kwargs)`, so it needs a function, not a class. `View.as_view()` is a class-only method that builds and returns exactly that: an inner function closing over the class and any `**initkwargs`. Each time a request arrives, that function instantiates the class with the initkwargs, calls `setup()` to attach `request`, `args` and `kwargs`, then calls `dispatch()`, which picks `get()`, `post()` and so on from the lowercased HTTP method. `as_view()` itself runs once, when the URLconf is loaded; it also validates the initkwargs, raising `TypeError` for a keyword that is not already a class attribute or that names an HTTP method. The returned function carries `view_class` and `view_initkwargs` so tools can tell which class sits behind a URL.

code

python · 19 lines
python
from django.http import HttpResponse
from django.urls import path
from django.views import View


class StatusView(View):
    message = "ok"

    def get(self, request, *args, **kwargs):
        return HttpResponse(self.message)


urlpatterns = [
    path("status/", StatusView.as_view()),
    path("status/maintenance/", StatusView.as_view(message="maintenance")),
]

# StatusView.as_view(colour="red")  -> TypeError when the URLconf loads
# urlpatterns[0].callback.view_class  -> StatusView

go deeper

for a junior

Recall that path() needs a callable, that as_view() produces it, and that each request gets a brand-new instance which runs setup() and then dispatch().

for a middle

Explain the closure: as_view() runs once at URLconf import and validates initkwargs, while the returned function instantiates, sets up and dispatches per request.

for a senior

Show you know what survives between requests: instance state never does, class-level state lives as long as the worker process, and initkwarg objects are shared by every instance.

for a principal

Frame as_view() as the reason CBVs can be configured per route; weigh how much configuration belongs in initkwargs versus subclasses for readability across a team.

## What the URL resolver expects Django's URL resolver has a very small contract for a **view**: it is a callable that takes an `HttpRequest` plus the arguments captured from the URL pattern, and returns an `HttpResponse`. A plain function satisfies that directly. A class does not: calling `MyView(request)` would only construct an object, and Django has no notion of "call the class and then find a method on it". `View.as_view()` bridges the gap. It turns a class into the function the resolver wants: ```python from django.urls import path from .views import StatusView urlpatterns = [ path("status/", StatusView.as_view(), name="status"), ] ``` Writing `path("status/", StatusView)` is a bug, and Django catches it: the system check framework reports **`urls.E009`** ("pass StatusView.as_view() instead of StatusView") when `runserver` or `manage.py check` runs. Without that check the resolver would call the class with the request as a positional argument, which `View.__init__()` (keyword arguments only) rejects with a `TypeError`. ## What as_view() builds `as_view()` is decorated with `classonlymethod`, so it can be called on the class but raises `AttributeError` if you try it on an instance. It runs **once**, when the URLconf module is imported, and returns an inner function usually shown as `view`. That function is what runs **on every request**: 1. **Instantiate** — `self = cls(**initkwargs)`. The base `View.__init__()` simply does `setattr(self, key, value)` for each initkwarg. 2. **Set up** — `self.setup(request, *args, **kwargs)` stores `self.request`, `self.args` and `self.kwargs`, and aliases `head` to `get` when the class defines `get()` but no `head()`. 3. **Sanity check** — if the instance has no `request` attribute afterwards, Django raises `AttributeError` asking whether you overrode `setup()` and forgot `super()`. 4. **Dispatch** — `return self.dispatch(request, *args, **kwargs)`, which looks up the handler named after `request.method.lower()`. The key consequence: **one instance per request**. Attributes you assign on `self` during a request die with that request; nothing on the instance survives into the next one. ## initkwargs: configuration at URLconf time Keyword arguments passed to `as_view()` override class attributes for that one URL, which lets one class serve several routes: | Call | Result | |---|---| | `StatusView.as_view()` | Uses the class attributes as written | | `StatusView.as_view(template_name="status/brief.html")` | Allowed if `template_name` is already an attribute of the class | | `StatusView.as_view(colour="red")` | `TypeError`: as_view only accepts arguments that are already attributes of the class | | `StatusView.as_view(get=some_function)` | `TypeError`: an HTTP method name is not accepted as a keyword | Both checks run inside `as_view()`, so the mistake surfaces as soon as the URLconf is loaded, not on the first request to that route. ## What the returned function carries The function returned by `as_view()` is decorated with a little metadata: - **`view_class`** — the class, so debugging tools and tests can ask which view serves a URL. - **`view_initkwargs`** — the dict of keyword arguments passed to `as_view()`. - **`__doc__` and `__module__`** — copied from the class. - **Attributes from `dispatch`** — the function's `__dict__` is updated from `cls.dispatch.__dict__`, which is how a flag such as the one `csrf_exempt` sets on `dispatch` reaches the URL-level callable. - **`__name__` is left as the inner function's name** on purpose; Django's comment says to use `view_class` to identify the view robustly. ## What the handlers can rely on Because `setup()` runs before `dispatch()`, every handler and every helper method on the instance can read the request context from `self` instead of threading it through arguments: - `self.request` — the `HttpRequest`, including `self.request.user` once authentication middleware has run. - `self.args` — positional values captured by a regex-based `re_path()` group without a name. - `self.kwargs` — named values captured by `path()` converters, such as `<int:pk>` becoming `self.kwargs["pk"]`. The handlers still receive `request`, `*args` and `**kwargs` as parameters as well, so both styles work; generic views mostly read from `self`. ## Why the design is this way - **Isolation** — a fresh instance per request means handlers can freely store request-specific state on `self` (`self.object`, `self.request`) without locking. - **Reuse** — the same class can be mounted at several URLs with different initkwargs. - **Cheap per-request cost** — instantiating a plain Python object is trivial next to template rendering or a database query. ## Common mistakes - Passing the class instead of `as_view()`, or calling `as_view` on an instance. - Expecting `__init__()` to see the request: it runs before `setup()`, with initkwargs only. - Assuming the instance is reused between requests and caching data on `self` "for next time" — it is thrown away. - Forgetting that **class attributes** are not per-request: the instance is new, the class is not, so mutating a list or dict defined on the class body leaks between requests.

  • When exactly does as_view() run, and when does the function it returns run?
    `as_view()` runs once, when the URLconf module is imported and the `urlpatterns` list is built. The function it returns runs on every matching request. That split is why initkwarg validation errors appear at URLconf load time, while everything in `setup()`, `dispatch()` and the handlers happens per request on a new instance.
  • Given only the resolved URL, how can a test find out which class-based view serves it?
    `resolve(path).func` is the function returned by `as_view()`, and it carries `view_class` and `view_initkwargs`. Comparing `resolve('/status/').func.view_class` with the expected class is more robust than checking `__name__`, which Django deliberately leaves as the inner function's name rather than copying the class name.

saying these in an interview costs you the question

  • Registering the class itself in path() and expecting Django to instantiate it, although the urls.E009 check rejects it
  • Believing one view instance is created at startup and reused for every request
  • Thinking as_view() accepts any keyword and silently ignores unknown ones
  • Expecting __init__() to receive the request object
  • Assuming class attributes are reset per request because the instance is new
open as a page

In Django, what do ListView and DetailView need for a property index and property page, and which template and context names do they default to?

level: juniorimportance: must knowfreq 68%

basics

~10 s

Set model = Property. ListView renders listings/property_list.html with object_list and property_list; DetailView needs a pk or slug from the URL and renders listings/property_detail.html with object and property.

open as a page

In a Django function view, how do you paginate a QuerySet with Paginator, and what does the template loop over?

level: juniorimportance: must knowfreq 55%

basics

~10 s

Wrap the ordered QuerySet in Paginator(queryset, per_page), fetch the requested page with get_page(request.GET.get('page')), and pass that Page to the template, which loops over it and uses has_next() and next_page_number() for links.

open as a page

In Django, how do you serve a mostly static About page with TemplateView, and how do you add data to its template context?

level: juniorimportance: must knowfreq 55%

basics

~10 s

Route to TemplateView.as_view(template_name="about.html"); it renders that template on GET. Add fixed values with extra_context, or subclass and override get_context_data(), calling super() first so URL kwargs, view and extra_context stay in the context.

open as a page

In a Django ListView or DetailView, when do you set queryset versus override get_queryset(), and why is a class-level queryset safe to share?

level: middleimportance: must knowfreq 58%

basics

~20 s

Set queryset for a filter fixed at import time; override get_queryset() when it depends on the request, user or URL. The class-level QuerySet is safe because the generic get_queryset() hands each request a copy via .all().

open as a page

In a Django CreateView for talk proposals, how do you set the speaker from request.user without exposing it as a form field?

level: middleimportance: must knowfreq 70%

basics

~10 s

Leave speaker out of fields and override form_valid(): set form.instance.speaker = self.request.user, then return super().form_valid(form), which saves the form, stores self.object and redirects to the success URL.

open as a page

In Django, why must LoginRequiredMixin be listed before UpdateView in a class-based view's base classes?

level: middleimportance: must knowfreq 62%

basics

~20 s

Access mixins do their check in dispatch() and then call super().dispatch(). Python looks methods up left to right, and View.dispatch() never calls super(), so a mixin placed after UpdateView is never reached and the page is served unprotected.

open as a page

In Django's CreateView and UpdateView, what is the difference between setting fields and setting form_class, and may you set both?

level: juniorimportance: should knowfreq 52%

basics

~20 s

Setting fields makes the view generate a ModelForm for its model with modelform_factory; form_class hands it a form you wrote yourself. Setting both raises ImproperlyConfigured, and so does setting neither on a CreateView or UpdateView.

open as a page

How does Django's View.dispatch() choose a handler method, and when does a class-based view answer 405 Method Not Allowed?

level: middleimportance: should knowfreq 45%

basics

~20 s

View.dispatch() lowercases request.method and, if it is in http_method_names, calls the method of that name. A name outside the list, or one with no handler defined, goes to http_method_not_allowed(), which returns 405 with an Allow header.

open as a page

How does Django's DetailView get_object() find its object using pk_url_kwarg, slug_url_kwarg and slug_field, and what happens when the lookup fails?

level: middleimportance: should knowfreq 45%

basics

~20 s

DetailView.get_object() filters get_queryset() by the URL's pk (pk_url_kwarg) or, failing that, filters the slug_field column by the URL's slug (slug_url_kwarg), then calls .get(). No match gives 404; neither kwarg raises AttributeError; duplicate slugs raise MultipleObjectsReturned.

open as a page

In Django's FormView and its model subclasses, when do you override get_form_kwargs() rather than get_initial()?

level: middleimportance: should knowfreq 45%

basics

~10 s

Override get_initial() to pre-fill default values on the unbound form; override get_form_kwargs() to change the form's constructor arguments, typically to pass extra ones such as the current user, always starting from super().get_form_kwargs().

open as a page

After a valid form, how do Django's FormView, CreateView or UpdateView, and DeleteView each decide which URL to redirect to?

level: middleimportance: should knowfreq 48%

basics

~10 s

FormView needs success_url or raises ImproperlyConfigured. CreateView and UpdateView format success_url with the saved object's fields, else call its get_absolute_url(). DeleteView formats success_url with no fallback, computed before deleting.

open as a page

When writing a custom Django class-based view mixin that adds template context, why must get_context_data() call super() and return the merged dict?

level: middleimportance: should knowfreq 44%

basics

~10 s

Several classes in a Django view contribute context through one cooperative get_context_data() chain. A mixin that skips super() or returns its own dict drops what the others add, such as object, form or page_obj.

open as a page

In Django's Paginator, how do get_page() and page() differ, and which exceptions can page() raise?

level: middleimportance: should knowfreq 42%

basics

~10 s

page() is strict: it raises PageNotAnInteger for a non-integer and EmptyPage for a number out of range, both subclasses of InvalidPage. get_page() catches those, returning page 1 or the last page instead.

open as a page

What does setting paginate_by on a Django ListView add to the template context, and which attributes tune the pagination?

level: middleimportance: should knowfreq 48%

basics

~10 s

With paginate_by set, ListView builds a Paginator and adds paginator, page_obj and is_paginated, and object_list becomes just the current page. page_kwarg, paginate_orphans, allow_empty and paginator_class tune it.

open as a page

Why does Django emit UnorderedObjectListWarning when paginating some QuerySets, and why is ordering by created_at alone still not enough?

level: middleimportance: should knowfreq 38%

basics

~20 s

Without ORDER BY, the database may return rows in any order, so LIMIT/OFFSET pages can repeat or skip rows; Paginator warns when QuerySet.ordered is False. Ties on a non-unique column cause the same problem, so add the primary key as a tie-breaker.

open as a page

How does Django's RedirectView build its target from url or pattern_name, and what happens to captured URL arguments and the query string?

level: middleimportance: should knowfreq 42%

basics

~20 s

RedirectView.get_redirect_url() interpolates URL kwargs into url with %-formatting, or else reverses pattern_name with the same captured args and kwargs. The query string is dropped unless query_string=True, and with neither url nor pattern_name it returns 410 Gone.

open as a page

On a Django forum, a class-based view's list class attribute starts showing one member's unread notices to other members; why does it leak, and how do you fix it?

level: seniorimportance: should knowfreq 50%

basics

~20 s

Django builds a new view instance per request, but the class lives for the whole process. Appending to a list defined on the class mutates one shared list that later requests in that process see. Build such state per request.

open as a page

On a Django real-estate site, agents open other agencies' unpublished listings by editing the pk in a DetailView URL; why, and how do you close it with the generic views' hooks?

level: seniorimportance: should knowfreq 42%

basics

~20 s

With model = Listing, DetailView looks the pk up in Listing._default_manager.all(), every row. Override get_queryset() in a mixin shared by the list, detail and edit views to return only rows the user may see; get_object() then 404s on everything else.

open as a page

After upgrading a Django project, audit logging in a DeleteView's overridden delete() no longer runs on POST; why, and where should it move?

level: seniorimportance: should knowfreq 38%

basics

~20 s

Since Django 4.0 DeleteView handles POST through FormMixin: post() validates a confirmation form and form_valid() does the deletion. An overridden delete() now runs only on HTTP DELETE, so the logic belongs in form_valid() or a shared helper.

open as a page

In a Django UpdateView for podcast episodes guarded by UserPassesTestMixin, why does an ownership test_func load the episode twice, and how do you fix it?

level: seniorimportance: should knowfreq 34%

basics

~10 s

UserPassesTestMixin runs test_func() inside dispatch(), before UpdateView's get() or post() sets self.object, so test_func must call get_object() and the handler then calls it again. Memoise get_object(), or scope get_queryset() to the owner.

open as a page

A Django ListView paginating two million audit-log rows is slow even on page 1; how do you find the cost and reduce it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Every paginated page runs Paginator.count, a SELECT COUNT(*) over all matching rows, before fetching the slice. On two million rows the count dominates; override count in a Paginator subclass (capped, cached or estimated) and set paginator_class, or drop page numbers.

open as a page

On a Django news site, a TemplateView's extra_context shows the same headlines and copyright year until the server restarts; why, and how do you fix it?

level: seniorimportance: should knowfreq 38%

basics

~20 s

extra_context is evaluated once, when the class body or URLconf loads, and the same dict is merged into every request's context. A computed year freezes, and a QuerySet stored there caches its rows on first render. Compute such values in get_context_data().

open as a page

What rule would you set for a Django team on when to use generic class-based views with mixins versus plain function views?

level: principalimportance: should knowfreq 40%

basics

~20 s

Use a generic class-based view when the page really is list, detail or create/update/delete and needs only a few hook overrides; write a function view when the flow is bespoke. Cap mixin depth, keep access mixins declarative, and review against that.

open as a page

In a Django class-based view, what is View.setup() for, and when would you override it instead of __init__() or dispatch()?

level: middleimportance: nice to knowfreq 28%

basics

~10 s

View.setup() runs on each new view instance before dispatch() and stores request, args and kwargs on self. Override it, calling super(), to derive per-request attributes every handler needs; init() never sees the request.

open as a page

In Django 6.1, which status codes can RedirectView send, and how do permanent and preserve_request choose between them?

level: middleimportance: nice to knowfreq 26%

basics

~10 s

RedirectView sends 302 by default, 301 with permanent=True, and, since Django 6.1, 307 or 308 when preserve_request=True, which tells the client to repeat the same method and body at the new URL.

open as a page

How do you combine Django's SingleObjectMixin with ListView to paginate one podcast's episodes, and which pitfalls must you handle?

level: seniorimportance: nice to knowfreq 22%

basics

~10 s

Subclass SingleObjectMixin and ListView, set self.object = self.get_object(queryset=Podcast.objects.all()) in get() before super().get(), return that podcast's episodes from get_queryset(), and add the podcast to the context yourself.

open as a page