1.APIView, generic views, and mixins
APIView 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.
Key features
- APIView.dispatch() is what actually runs DRF's authentication/permission/throttle checks before calling get()/post()/etc. — a plain Django View subclass calling these methods manually would skip that machinery entirely
- request.data (not request.POST) is DRF's parsed-body access on an APIView — it works for JSON, form data, and multipart alike, unlike request.POST, which only handles form-encoded/multipart bodies
- Each generic view mixes GenericAPIView (queryset/serializer_class handling, pagination hooks, filtering hooks) with one or more of the five model mixins — ListAPIView = ListModelMixin + GenericAPIView with get() calling self.list()
- ListCreateAPIView and RetrieveUpdateDestroyAPIView are the two most commonly used COMBINED generic views — list+create on a collection endpoint, retrieve+update+delete on a detail endpoint, matching the conventional URL pattern of /items/ vs /items/<pk>/
- Each mixin method (list(), create(), retrieve(), update(), destroy()) internally calls get_queryset()/get_object()/get_serializer() — overriding one of THOSE hook methods is usually the correct way to customize behavior, rather than overriding list()/create() themselves
- UpdateModelMixin.update() handles both PUT (full update) and PATCH (partial update, via partial=True passed to the serializer) through the same method — partial is determined from the request method, not a separate mixin
The view spectrum, least to most generated
- 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
Command
class MyView(generics.ListCreateAPIView):
queryset = Model.objects.all()
serializer_class = MySerializerAPIView 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.