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.
- ListModelMixin - Supplies the
list()method for displaying collections of model instances. - CreateModelMixin - Provides the
create()method for adding new model instances. - RetrieveModelMixin - Offers the
retrieve()method for fetching individual model details. - UpdateModelMixin - Includes the
update()method for modifying existing model instances. - 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()),
]