APIView, generic views, and mixins
coreintermediateAPIView is DRF's equivalent of Django's View — the base class every other DRF class-based view builds on, adding DRF-specific request/response handling (request.data instead of request.POST, content negotiation, DRF's own authentication/permission/throttle checks) but leaving get()/post()/etc. entirely up to you, same as plain Django. Generic views (ListAPIView, RetrieveAPIView, CreateAPIView, etc.) are pre-built APIView subclasses that already implement the common CRUD patterns via mixins — each generic view is really just a specific mixin combination plus a matching HTTP-method handler. Mixins (ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin) are the actual reusable behavior — a .list()/.create()/.retrieve()/.update()/.destroy() method each — that generic views compose together, and that a custom view can mix in selectively for a non-standard combination.
Think of it as
DRF's view hierarchy exists to let a developer choose exactly how much is generated versus hand-written, on a spectrum: APIView gives the least (DRF's request/response/auth plumbing, nothing else) — appropriate when a view's logic genuinely doesn't map to CRUD-on-one-model. A mixin (ListModelMixin, etc.) gives one specific, reusable behavior as a method (.list(), .create()) that still needs a class combining it with APIView and wiring get()/post() to call it — appropriate for a custom combination the pre-built generic views don't already cover. A generic view (ListCreateAPIView, RetrieveUpdateDestroyAPIView) gives the FULL combination already wired up — appropriate for the common case where a view really is just "list/create/retrieve/update/delete this model," which is most CRUD API endpoints. This layered design is why understanding mixins matters even when generic views are used 90% of the time: the moment a real endpoint needs something slightly non-standard (list and create, but a custom retrieve), knowing that ListCreateAPIView is just ListModelMixin + CreateModelMixin + GenericAPIView, wired with matching get()/post() methods, is what makes it obvious how to build the same shape by hand instead of fighting a generic view that doesn't quite fit.
What we're doing: Build a custom view (list + a non-standard bulk-archive action) by combining mixins manually, since no single generic view covers this exact combination.
- 1
- Only ListModelMixin is combined with GenericAPIView — there's no CreateModelMixin here, since POST does something entirely custom instead of creating an Article.
- 5
- get() explicitly calls self.list() — a mixin's method is never called automatically; the handler method (get/post/etc.) still has to invoke it, same as a full generic view does internally.
Why this works: This endpoint's shape (list is standard, but POST means "bulk archive" rather than "create one Article") doesn't match any pre-built generic view — composing ListModelMixin with a hand-written post() gets exactly the needed behavior without duplicating list()'s pagination/filtering/serialization logic from scratch.
Overriding a mixin's top-level method (e.g. list()) to add a small tweak, instead of the more specific hook method it already calls
Wrong
Better
What you see: A large amount of DRF's own pagination/filtering/serialization logic gets copy-pasted and reimplemented just to change one thing — and that copy silently drifts out of sync with DRF's actual list() behavior on the next DRF version upgrade, since it's no longer calling the real implementation at all.
Why: list() (and create(), retrieve(), etc.) are deliberately implemented in terms of smaller, overridable hook methods — get_queryset(), get_serializer(), get_object(), filter_queryset() — specifically so a narrow customization (which queryset, which serializer) doesn't require reimplementing the whole method. Overriding the narrowest hook that actually needs to change is the documented, low-risk way to customize a generic view.
- APIView — request/response/auth plumbing only — every handler hand-written
- A mixin + GenericAPIView — one behavior (.list(), .create()) — handlers still wired manually
- A generic view (ListAPIView, ...) — the full common combination already wired
The view spectrum, least to most generated
Together
Remember: APIView gives DRF's request/response/auth plumbing with every handler hand-written; a mixin gives one reusable behavior (.list()/.create()/etc.) still needing manual wiring; a generic view gives the full common combination pre-wired. Customize a generic view via its narrower hook methods (get_queryset(), get_serializer(), get_object()), not by overriding list()/create() wholesale. Always use request.data, never request.POST, on a DRF view.
See also: viewsets and routers · customizing generic view behavior · drf architecture and modelserializer

