Using Mixins in Django REST Framework to Extend View Functionality
This article explains how to use Django REST Framework mixins to extend view functionality, providing step‑by‑step code examples for list, retrieve, create, update, delete, custom authentication, permissions, versioning, conditional rendering, and custom exception handling.
In Django REST Framework (DRF), mixins are a common design pattern for extending view functionality. By inheriting from mixin classes you can compose reusable behaviors for ViewSets and generic views.
Example: Using ListModelMixin and RetrieveModelMixin
from rest_framework import mixins
from rest_framework import generics
from django.contrib.auth.models import User
from .serializers import UserSerializer
class UserListView(generics.GenericAPIView, mixins.ListModelMixin, mixins.RetrieveModelMixin):
queryset = User.objects.all()
serializer_class = UserSerializer
def get(self, request, *args, **kwargs):
if 'pk' in kwargs: # detailed view
return self.retrieve(request, *args, **kwargs)
else: # list view
return self.list(request, *args, **kwargs)
# URL configuration
from django.urls import path
from .views import UserListView
urlpatterns = [
path('users/', UserListView.as_view(), name='user_list'),
path('users/
/', UserListView.as_view(), name='user_detail'),
]Example: Using CreateModelMixin and UpdateModelMixin
from rest_framework import mixins
from rest_framework import generics
from django.contrib.auth.models import User
from .serializers import UserSerializer
class UserCRUDView(generics.GenericAPIView, mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin):
queryset = User.objects.all()
serializer_class = UserSerializer
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
# URL configuration
from django.urls import path
from .views import UserCRUDView
urlpatterns = [
path('users/', UserCRUDView.as_view(), name='user_create'),
path('users/
/', UserCRUDView.as_view(), name='user_update_delete'),
]Example: Combining ListModelMixin and CreateModelMixin
from rest_framework import mixins
from rest_framework import generics
from django.contrib.auth.models import User
from .serializers import UserSerializer
class UserListCreateView(generics.GenericAPIView, mixins.ListModelMixin, mixins.CreateModelMixin):
queryset = User.objects.all()
serializer_class = UserSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
# URL configuration
from django.urls import path
from .views import UserListCreateView
urlpatterns = [
path('users/', UserListCreateView.as_view(), name='user_list_create'),
]Example: RetrieveModelMixin and UpdateModelMixin
from rest_framework import mixins
from rest_framework import generics
from django.contrib.auth.models import User
from .serializers import UserSerializer
class UserDetailUpdateView(generics.GenericAPIView, mixins.RetrieveModelMixin, mixins.UpdateModelMixin):
queryset = User.objects.all()
serializer_class = UserSerializer
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
# URL configuration
from django.urls import path
from .views import UserDetailUpdateView
urlpatterns = [
path('users/
/', UserDetailUpdateView.as_view(), name='user_detail_update'),
]Example: Using DestroyModelMixin
from rest_framework import mixins
from rest_framework import generics
from django.contrib.auth.models import User
from .serializers import UserSerializer
class UserDeleteView(generics.GenericAPIView, mixins.DestroyModelMixin):
queryset = User.objects.all()
serializer_class = UserSerializer
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
# URL configuration
from django.urls import path
from .views import UserDeleteView
urlpatterns = [
path('users/
/', UserDeleteView.as_view(), name='user_delete'),
]These examples demonstrate how DRF mixin classes can be combined to quickly build views with the desired capabilities, improving code readability and maintainability.
Advanced Example: Custom Authentication and Permission
from django.contrib.auth.models import User
from rest_framework import mixins, generics, authentication, permissions
from .serializers import UserSerializer
# Custom authentication backend
class CustomAuthentication(authentication.BaseAuthentication):
def authenticate(self, request):
# Implement custom token extraction and validation here
return None
# Custom permission class
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj == request.user
class UserDetailView(generics.GenericAPIView, mixins.RetrieveModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin):
queryset = User.objects.all()
serializer_class = UserSerializer
authentication_classes = [CustomAuthentication]
permission_classes = [IsOwnerOrReadOnly]
def get(self, request, *args, **kwargs):
return self.retrieve(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
# URL configuration
from django.urls import path
from .views import UserDetailView
urlpatterns = [
path('users/
/', UserDetailView.as_view(), name='user_detail'),
]Versioning
from rest_framework import mixins, generics, versioning
from .serializers import UserSerializer
class UserListView(generics.GenericAPIView, mixins.ListModelMixin, mixins.CreateModelMixin):
queryset = User.objects.all()
serializer_class = UserSerializer
versioning_class = versioning.URLPathVersioning
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
# URL configuration
from django.urls import path, re_path
from .views import UserListView
urlpatterns = [
re_path(r'^users/v(?P
[1-9])/$', UserListView.as_view(), name='user_list'),
]Conditional Rendering
from rest_framework import mixins, generics, renderers
from .serializers import UserSerializer
class UserListView(generics.GenericAPIView, mixins.ListModelMixin, mixins.CreateModelMixin):
queryset = User.objects.all()
serializer_class = UserSerializer
renderer_classes = [renderers.JSONRenderer, renderers.XMLRenderer]
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
# URL configuration
from django.urls import path
from .views import UserListView
urlpatterns = [
path('users/', UserListView.as_view(), name='user_list'),
]Custom Exception Handling
from django.http import JsonResponse
from rest_framework import status
from rest_framework.exceptions import APIException
from rest_framework.views import exception_handler
def custom_exception_handler(exc, context):
response = exception_handler(exc, context)
if response is not None:
response.data['status_code'] = response.status_code
return response
class CustomAPIException(APIException):
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
default_detail = 'An unexpected error occurred.'
default_code = 'unexpected_error'
class UserListView(generics.GenericAPIView, mixins.ListModelMixin, mixins.CreateModelMixin):
queryset = User.objects.all()
serializer_class = UserSerializer
def get(self, request, *args, **kwargs):
if not request.user.is_authenticated:
raise CustomAPIException(detail='You need to log in to access this resource.')
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
# URL configuration
from django.urls import path
from .views import UserListView
urlpatterns = [
path('users/', UserListView.as_view(), name='user_list'),
]Complex Logic with APIView and Mixins
from rest_framework import mixins, generics, status
from rest_framework.response import Response
from .serializers import UserSerializer
class UserManagementView(generics.GenericAPIView, mixins.ListModelMixin, mixins.CreateModelMixin, mixins.UpdateModelMixin, mixins.DestroyModelMixin):
queryset = User.objects.all()
serializer_class = UserSerializer
def get(self, request, *args, **kwargs):
if 'pk' in kwargs:
return self.retrieve(request, *args, **kwargs)
return self.list(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
if 'pk' in kwargs:
return self.partial_update(request, *args, **kwargs)
return self.create(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
# URL configuration
from django.urls import path
from .views import UserManagementView
urlpatterns = [
path('users/', UserManagementView.as_view(), name='user_management'),
path('users/
/', UserManagementView.as_view(), name='user_management_detail'),
]These examples show how to leverage DRF mixins for advanced features such as custom authentication, permission checks, API versioning, content negotiation, custom error handling, and complex view logic, enabling developers to build efficient and maintainable APIs.
Test Development Learning Exchange
Test Development Learning Exchange
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.