In a Django class-based view, what is View.setup() for, and when would you override it instead of __init__() or dispatch()?
answer
- runs before dispatch
- attaches request, args, kwargs
- __init__ never sees the request
- forget super() and it fails
- access mixins run later
basics
~10 sView.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.
solid answer
~40 sThe function returned by `as_view()` calls `cls(**initkwargs)`, then `setup(request, *args, **kwargs)`, then `dispatch()`. `__init__()` receives only initkwargs, so it cannot read the request; `setup()` is the first hook that can. The base `setup()` sets `self.request`, `self.args` and `self.kwargs` and aliases `head` to `get`; override it to derive attributes shared by every handler, such as a value parsed from a URL kwarg, and always call `super().setup(...)` first — if `request` is missing afterwards Django raises `AttributeError` asking whether you forgot `super()`. Prefer `dispatch()` for anything that should run after access checks, because mixins such as `LoginRequiredMixin` enforce access in `dispatch()`, and `setup()` runs before them. In tests, `view = MyView(); view.setup(request)` lets you call a single method directly.
code
python · 11 linesfrom django.test import RequestFactory, SimpleTestCase
from .views import ExportView
class ExportViewSetupTests(SimpleTestCase):
def test_format_is_normalised(self):
request = RequestFactory().get("/export/CSV/")
view = ExportView()
view.setup(request, fmt="CSV")
self.assertEqual(view.export_format, "csv")go deeper
Recall that setup() is where request, args and kwargs get attached to the view instance, and that overrides must call super().
Explain the order constructor, setup(), dispatch(), handler, and why the request is unavailable in init().
Point out that setup() runs before access mixins enforce anything in dispatch(), and keep permission-sensitive or expensive work out of it.
Set a team convention for which hook owns what, so reviewers can spot work that runs before access checks at a glance.
## Where setup() sits in the lifecycle When a request reaches a Django class-based view, the function returned by `View.as_view()` runs four steps in order: 1. `self = cls(**initkwargs)` — the **constructor**, fed only the keyword arguments given to `as_view()` in the URLconf. 2. `self.setup(request, *args, **kwargs)` — the **per-request initialiser**. 3. A check that `self.request` now exists. 4. `self.dispatch(request, *args, **kwargs)` — the **router** to `get()`, `post()` and the other handlers. `setup()` exists so that there is one clearly named place, before any handler or `dispatch()` override, where the request is attached to the instance. ## What the base setup() does The default implementation is short: - If the class defines `get` but not `head`, it sets `self.head = self.get`, which is why `HEAD` requests work on read views. - It stores `self.request = request`. - It stores `self.args` and `self.kwargs`, the positional and keyword arguments captured from the URL pattern. After it returns, every method on the view can read `self.request`, `self.kwargs["slug"]` and so on without having them passed in. ## Choosing the right hook | Hook | Sees the request? | Runs relative to access mixins | Good for | |---|---|---|---| | `__init__(**kwargs)` | No | Before | Rarely anything; initkwargs are already applied by the base constructor | | `setup(request, ...)` | Yes | **Before** `LoginRequiredMixin`, `PermissionRequiredMixin` and `UserPassesTestMixin` | Cheap derived attributes every handler and mixin may read | | `dispatch(request, ...)` | Yes | Mixins' `dispatch()` runs around yours depending on base-class order | Cross-cutting logic around every verb | | `get()` / `post()` | Yes | After the access check has passed | Verb-specific work | The ordering column is the one that bites. The access mixins in `django.contrib.auth.mixins` enforce their checks by overriding `dispatch()`. Since `setup()` runs **before** `dispatch()`, anything in `setup()` executes for anonymous and unauthorised users too. So: - **Do** parse and normalise values from `self.kwargs` or the query string in `setup()`. - **Do not** run expensive queries, touch per-user data, or assume `request.user` is authenticated in `setup()`; move that to `dispatch()` after `super()`, or into the handler. ## Overriding it correctly ```python from django.views import View class ExportView(View): def setup(self, request, *args, **kwargs): super().setup(request, *args, **kwargs) self.export_format = kwargs.get("fmt", "csv").lower() ``` Call `super().setup(...)` **first**, so `self.request`, `self.kwargs` and the `head` alias exist before your code uses them. If you forget `super()` entirely, the `as_view()` function notices that the instance has no `request` attribute and raises `AttributeError` with the message "Did you override setup() and forget to call super()?" — a deliberate guard, because the failure would otherwise surface later as a confusing error deep inside a mixin. ## Why not __init__()? The base `View.__init__()` just assigns each initkwarg onto the instance. Overriding it buys nothing that `setup()` does not, and has two costs: the request is not available yet, and a signature that forgets `**kwargs` breaks every `as_view(...)` override for that class. There is almost never a reason to touch `__init__()` on a Django view. ## setup() in tests Because `setup()` is separate from `dispatch()`, a unit test can build a view, attach a request and call one method without going through the URL resolver or middleware: - Build a request with `RequestFactory().get("/")`. - Instantiate the view with `MyView()` and call `view.setup(request)`. - Call the method under test, for example `view.get_context_data()`, and assert on the result. This is the pattern Django's own testing documentation shows, and it keeps tests of context building independent of rendering. ## setup() versus dispatch() overrides Both hooks see the request, so the choice comes down to timing and intent: - `setup()` is for **state**: attributes that later code reads, computed without side effects. - `dispatch()` is for **control flow**: code that may short-circuit with its own response (a redirect, a 403) or wrap every handler, which is why the access mixins live there. - A `dispatch()` override that needs a value computed in `setup()` can rely on it, because `setup()` has always run first. ## Common mistakes - Overriding `setup()` without `super()`. - Loading user-specific data in `setup()` and leaking it to anonymous callers before `LoginRequiredMixin` rejects them. - Doing work in `__init__()` that needs `self.request`.
- Your setup() override loads the current user's workspace, and anonymous visitors now trigger errors. Why?`setup()` runs before `dispatch()`, and `LoginRequiredMixin` enforces authentication in `dispatch()`. So the workspace lookup runs for anonymous users before the mixin can redirect them, hitting `AnonymousUser`. Move user-dependent work into the handler or into a `dispatch()` override that calls it after `super()`, and keep `setup()` for request-independent derivations.
- What error do you get if a setup() override forgets to call super(), and where is it raised?The function returned by `as_view()` checks `hasattr(self, "request")` right after `setup()`. Without `super()`, the attribute is never set, so it raises `AttributeError` with a message asking whether you overrode `setup()` and forgot to call `super()`. It fires on the first request to that view, before `dispatch()` runs.
saying these in an interview costs you the question
- Overriding __init__() to read self.request
- Believing setup() runs after LoginRequiredMixin has checked the user
- Calling super().setup() last, after code that reads self.kwargs
- Thinking setup() runs once per URL pattern rather than once per request
- Saying a missing super() in setup() is silently harmless