Skip to content
thesarfo

Note

Mixins

How ListModelMixin and CreateModelMixin encapsulate the repeated list/create pattern shared by different views.

views 0

Now if you take a look at the way we get a product or create a product below

'''this is its CBV implementation'''
class ProductList(APIView):
def get(self, request):
queryset = Product.objects.select_related('collection').all()
serializer = ProductSerializer(queryset, many=True, context={'request': request})
return Response(serializer.data)
def post(self, request):
serializer = ProductSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)

And you compare it to how we get and create a collection below

def collection_list(request):
if request.method == 'GET':
'''get all collections, and annotate them with the number of products in each collection'''
queryset = Collection.objects.annotate(products_count=Count('products')).all()
serializer = CollectionSerializer(queryset, many=True)
return Response(serializer.data)
elif request.method == 'POST':
serializer = CollectionSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)

You will notice that aside some 1 or 2 minor differences, they are all implemented with the same pattern. Now this is an advantage of Class Based Views - its reusability.

This is where mixins come into place, a mixin is a class that encapsulates the pattern of code like the ones above.

To use mixins you need to import them from ‘rest_framework.mixins’…you can import the ListModelMixin and CreateModelMixin for now

from rest_framework.mixins import ListModelMixin and CreateModelMixin

Below is how the ListModelMixin pattern works, if you look closely you can see that it creates a queryset, serializes the queryset and returns the serialized data. Just like what we do get a Product.

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)

This is the mixin logic for creating a resource. ie the CreateModelMixin

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 {}