Implementing CRUD Operations with Django REST Framework's GenericAPIView and Mixin Classes

In Django REST Framework, combining GenericAPIView with specialized Mixin classes enables developers to create powerful and reusable views for API endpoints. These Mixin classes encapsulate common behaviors such as listing, creating, retrieving, updating, and deleting resources, while GenericAPIView provides the foundational structure for handling HTTP requests. Below, we explore five essential Mixin classes that can be combined with GenericAPIView to streamline API development.

  1. ListModelMixin - Supplies the list() method for displaying collections of model instances.
  2. CreateModelMixin - Provides the create() method for adding new model instances.
  3. RetrieveModelMixin - Offers the retrieve() method for fetching individual model details.
  4. UpdateModelMixin - Includes the update() method for modifying existing model instances.
  5. DestroyModelMixin - Delivers the destroy() method for removing model instances.

These extension classes must be used with the GenericAPIView base class, as their implementations rely on methods provided by GenericAPIView for serialization and database querying. They offer pre-implemented handling for common CRUD operations, allowing developers to reduce boilerplate code by inheriting the appropriate classes.

1. ListModelMixin

The list view extension class provides a list(request, *args, **kwargs) method to quickly implement list endpoints, returning a 200 HTTP status code. This mixin's list method automatically handles data filtering and pagination.

class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

Here's an implementation for a ProductView using this mixin:

from rest_framework import status
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from .models import Product
from .serializers import ProductSerializer
from rest_framework.mixins import ListModelMixin

class ProductView(GenericAPIView, ListModelMixin):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def get(self, request):
        return self.list(request)

2. CreateModelMixin

The create view extension class offers a create(request, *args, **kwargs) method to implement resource creation endpoints, returning a 201 status code on success. If the serializer validation fails, it returns a 400 error.

class CreateModelMixin:
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}

Example implementation for a ProductView:

class ProductView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)

3. RetrieveModelMixin

The detail view extension class provides a retrieve(request, *args, **kwargs) method to quickly implement endpoints that return a single data object. Returns 200 if found, otherwise 404.

class RetrieveModelMixin:
    """
    Retrieve a model instance.
    """
    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

Example implementation for a ProductDetailView:

class ProductDetailView(GenericAPIView, RetrieveModelMixin):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def get(self, request, pk):
        return self.retrieve(request, pk)

4. UpdateModelMixin

The update view extension class supplies an update(request, *args, **kwargs) method to implement endpoints for updating existing data objects. It also provides a partial_update(request, *args, **kwargs) method for partial updates. Returns 200 on success, 400 on validation failure.

class UpdateModelMixin:
    """
    Update a model instance.
    """
    def update(self, request, *args, **kwargs):
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        return Response(serializer.data)

    def perform_update(self, serializer):
        serializer.save()

    def partial_update(self, request, *args, **kwargs):
        kwargs['partial'] = True
        return self.update(request, *args, **kwargs)

Example implementation for a ProductDetailView:

class ProductDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def get(self, request, pk):
        return self.retrieve(request, pk)

    def put(self, request, pk):
        return self.update(request, pk)

5. DestroyModelMixin

The delete view extension class provides a destroy(request, *args, **kwargs) method to quickly implement endpoints for removing existing data objects. Returns 204 on success, 404 if not found.

class DestroyModelMixin:
    """
    Destroy a model instance.
    """
    def destroy(self, request, *args, **kwargs):
        instance = self.get_object()
        self.perform_destroy(instance)
        return Response(status=status.HTTP_204_NO_CONTENT)

    def perform_destroy(self, instance):
        instance.delete()

Example implementation for a ProductDetailView:

class ProductDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def get(self, request, pk):
        return self.retrieve(request, pk)

    def put(self, request, pk):
        return self.update(request, pk)

    def delete(self, request, pk):
        return self.destroy(request, pk)

Here's a complete implementation using GenericAPIView with the view extension classes to implement basic API endpoints:

from rest_framework import serializers
from rest_framework.response import Response
from .models import Product
from rest_framework.generics import GenericAPIView
from rest_framework import status
from rest_framework.mixins import ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin

class ProductSerializer(serializers.ModelSerializer):
    """Product data serializer"""
    class Meta:
        model = Product
        fields = '__all__'

class ProductListView(GenericAPIView, ListModelMixin, CreateModelMixin):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def get(self, request):
        return self.list(request)

    def post(self, request):
        return self.create(request)

class ProductDetailView(GenericAPIView, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin):
    queryset = Product.objects.all()
    serializer_class = ProductSerializer

    def get(self, request, pk):
        return self.retrieve(request, pk)

    def put(self, request, pk):
        return self.update(request, pk)

    def delete(self, request, pk):
        return self.destroy(request, pk)

The corresponding URL configuration remains:

urlpatterns = [
    path('products/', ProductListView.as_view()),
    re_path('products/(?P<pk>\d+)/', ProductDetailView.as_view()),
]

Tags: Django REST Framework GenericAPIView Mixin Classes CRUD Operations API Development

Posted on Mon, 15 Jun 2026 16:10:09 +0000 by jmr3460