What rule would you set for a Django team on when to use generic class-based views with mixins versus plain function views?
answer
- fit to a generic, not taste
- count the overridden hooks
- one generic group per view
- readability for the next reader
basics
~20 sUse 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.
solid answer
~40 sI would make the rule about fit, not taste. Generic views earn their place on CRUD pages: `ListView`, `DetailView`, `CreateView`, `UpdateView`, `DeleteView` with access mixins on the left and one or two hooks such as `get_queryset()` or `form_valid()` overridden. Once a view overrides many hooks, combines generic groups (a single-object mixin with a list view, two forms on one page), or needs tracing through several files of mixins to see what `post()` does, a function view is clearer: the whole flow reads top to bottom. I would add guardrails: a short list of approved project mixins, access control always declared at the top of the class, and a test per view that checks anonymous and wrong-user access. The trade-off is consistency and less boilerplate against explicit, local code.
go deeper
Know that both styles are current Django and that generic views shine on standard list, detail and edit pages.
Explain which hooks a generic view offers and when overriding them stays simpler than writing the flow by hand.
Point to concrete smells: many overridden hooks, crossed generic groups, get() or post() rewritten, reviewers needing the MRO.
Set the rule and its guardrails (approved mixins, declarative access, per-view access tests, a depth budget) and own the consistency versus locality trade-off.
## Why this is a judgement call Django supports both styles fully. A function view takes a request and returns a response; everything happens in one visible body. A class-based view spreads behaviour across hook methods inherited from mixins, which removes boilerplate when the page fits a generic pattern and hides control flow when it does not. Neither is correct in general, so a team needs a rule it can apply in review. ## Signals that a generic view fits - The page is recognisably list, detail, create, update, delete or a form with a redirect. - The customisation fits in one or two hooks: narrowing `get_queryset()`, setting an owner in `form_valid()`, adding a key in `get_context_data()`. - Access control is expressible with `LoginRequiredMixin`, `PermissionRequiredMixin` or a single `test_func()`. - Several views share behaviour that a small, tested project mixin can carry. ## Signals that it does not - More than a handful of overridden hooks, especially overrides of `get()` or `post()` themselves. - Mixing generic groups, which the Django docs explicitly warn against: a single-object mixin with a list view, or a form mixin bolted onto a detail view. - Two forms on one page, a multi-step flow, or branching responses (HTML, a redirect, a file) that the generic flow was not designed for. - A reviewer needs the MRO printed to know which `dispatch()` or `get_context_data()` runs. ## A rule a team can apply | Situation | Default | |---|---| | Standard CRUD page, 0-2 hooks overridden | Generic class-based view | | Standard page plus shared cross-cutting behaviour | Generic view plus an approved project mixin | | Bespoke flow, several forms, unusual responses | Function view | | Mixins from two generic groups needed | Function view, or split into two views | Guardrails that make either style safe: 1. **Approved mixins only.** A short, documented list of project mixins, each single-purpose and cooperative with `super()`. 2. **Declarative access at the top.** Access mixins leftmost, or the equivalent decorator on a function view; never an ad-hoc check deep in a handler. 3. **Access tests per view.** Anonymous and wrong-user requests asserted for every protected URL, which catches a misordered mixin or a forgotten decorator. 4. **A depth budget.** For example, no more than two custom mixins per view before the design is discussed. ## The trade-offs to own - **Consistency versus locality.** Generic views make every CRUD page look alike; function views keep each page's logic in one place. - **Onboarding.** New developers read function views immediately; generic views require learning the hook names and their order. - **Refactoring cost.** Moving a grown class-based view to a function is cheap early and expensive once other views inherit from it. A reasonable team lands on generic views for the boring majority of pages and function views for the interesting minority, and revisits the rule when the mixin list starts growing.
- What would make you convert an existing class-based view to a function view?When it overrides `get()` or `post()` wholesale, combines mixins from different generic groups, or reviewers keep misreading which hook runs. At that point the generic flow is no longer doing work for you, and a function view that reads top to bottom is cheaper to maintain.
- How do you keep access control consistent across both styles?Declare it in one visible place per view: access mixins leftmost on classes, the matching decorators on functions. Back it with a project-wide login requirement and a test per protected URL that checks anonymous and wrong-user responses.
saying these in an interview costs you the question
- Class-based views are always better because they are object-oriented
- Function views are legacy and should be migrated to classes
- Deep mixin stacks are fine as long as tests pass today
- Mixing generic groups in one view is the recommended way to reuse code