skip to content

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%

answer

  1. fit to a generic, not taste
  2. count the overridden hooks
  3. one generic group per view
  4. readability for the next reader

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.

solid answer

~40 s

I 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

for a junior

Know that both styles are current Django and that generic views shine on standard list, detail and edit pages.

for a middle

Explain which hooks a generic view offers and when overriding them stays simpler than writing the flow by hand.

for a senior

Point to concrete smells: many overridden hooks, crossed generic groups, get() or post() rewritten, reviewers needing the MRO.

for a principal

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