DRF Generic Views & Mixins

When a full ViewSet is too much, generic views give you one endpoint with exactly the behaviour you want.

ViewSets are great for uniform CRUD, but sometimes you want a single, explicit endpoint — a read-only list, or a create-only intake endpoint. DRF's generic views are the middle ground between raw APIView and a full ModelViewSet.

The building blocks

Each generic view combines GenericAPIView (which supplies get_queryset, get_serializer, pagination) with one or more mixins:

ClassHTTP
ListAPIView / CreateAPIViewGET list / POST
RetrieveAPIViewGET one
ListCreateAPIViewGET list + POST
RetrieveUpdateDestroyAPIViewGET + PUT/PATCH + DELETE one
from rest_framework.generics import ListCreateAPIView

class BookListCreate(ListCreateAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer
    # GET  /books/  -> list
    # POST /books/  -> create

They share every hook a ViewSet has (get_queryset, perform_create, filtering, pagination), so everything you learned there transfers directly. Choose generics when you want URLs and behaviour spelled out one endpoint at a time; choose ViewSets when a router-generated CRUD set is exactly right.

Example

Example · python
from rest_framework import generics, filters
from rest_framework.permissions import IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend


class ProductListCreateView(generics.ListCreateAPIView):
    serializer_class = ProductSerializer
    permission_classes = [IsAuthenticated]
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    filterset_fields = ['category', 'in_stock']
    search_fields = ['name', 'sku']
    ordering_fields = ['price', 'created']
    ordering = ['-created']

    def get_queryset(self):
        # Only products belonging to the caller's organisation.
        return (
            Product.objects
            .filter(org=self.request.user.org)
            .select_related('category')
        )

    def perform_create(self, serializer):
        serializer.save(org=self.request.user.org, created_by=self.request.user)


class ProductDetailView(generics.RetrieveUpdateDestroyAPIView):
    serializer_class = ProductSerializer
    permission_classes = [IsAuthenticated]

    def get_queryset(self):
        return Product.objects.filter(org=self.request.user.org)

    def perform_destroy(self, instance):
        # Soft-delete instead of a hard DELETE.
        instance.is_active = False
        instance.save(update_fields=['is_active'])


# urls.py
# urlpatterns = [
#     path('products/', ProductListCreateView.as_view()),
#     path('products/<int:pk>/', ProductDetailView.as_view()),
# ]

When to use it

  • A developer uses ListCreateAPIView to provide both GET (list) and POST (create) on a single endpoint class.
  • A team uses RetrieveUpdateDestroyAPIView for a resource detail endpoint that supports all three methods.
  • A developer overrides perform_create() in a generic view to automatically set the author from request.user.

More examples

ListCreateAPIView

Handles GET (list) and POST (create) on /posts/; perform_create injects the logged-in author.

Example · python
from rest_framework.generics import ListCreateAPIView

class PostListCreateView(ListCreateAPIView):
    queryset = Post.objects.filter(draft=False)
    serializer_class = PostSerializer

    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

RetrieveUpdateDestroyAPIView

Provides GET, PUT/PATCH, and DELETE on /posts/{pk}/ with ownership-based permission.

Example · python
from rest_framework.generics import RetrieveUpdateDestroyAPIView

class PostDetailView(RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = [IsAuthenticated, IsOwnerOrReadOnly]

Wire generic views in urls.py

Maps each generic view to a URL pattern; no router needed for simple two-URL REST resources.

Example · python
from django.urls import path
from .views import PostListCreateView, PostDetailView

urlpatterns = [
    path("posts/", PostListCreateView.as_view(), name="post-list"),
    path("posts/<int:pk>/", PostDetailView.as_view(), name="post-detail"),
]

Discussion

  • Be the first to comment on this lesson.