Let's say we have model Travel with many related fields:
class Travel(models.Model):
tags = models.ManyToManyField(
Tag,
related_name='travels', )
route_places = models.ManyToManyField(
RoutePlace,
related_name='travels', )
coordinate = models.ForeignKey(
Coordinate,
related_name='travels', )
date_start = models.DateField()
And we want to build CRUD in /travels via view ViewSet.
Here is the simple viewset:
class TravelViewset(viewsets.ModelViewSet):
queryset = Travel.objects.all()
serializer_class = TravelSerializer
Problem with this ViewSet is we have many related fields in our Travel model, so Django will hit db for every Travel instance. We can call select_related and prefetch_related directly in queryset attribute, but what if we want to separate serializers for list, retrieve, create.. actions of ViewSet.
So we can put this logic in one mixin and inherit from it:
class QuerySerializerMixin(object):
PREFETCH_FIELDS = [] # Here is for M2M fields
RELATED_FIELDS = [] # Here is for ForeignKeys
@classmethod
def get_related_queries(cls, queryset):
# This method we will use in our ViewSet
# for modify queryset, based on RELATED_FIELDS and PREFETCH_FIELDS
if cls.RELATED_FIELDS:
queryset = queryset.select_related(*cls.RELATED_FIELDS)
if cls.PREFETCH_FIELDS:
queryset = queryset.prefetch_related(*cls.PREFETCH_FIELDS)
return queryset
class TravelListSerializer(QuerySerializerMixin, serializers.ModelSerializer):
PREFETCH_FIELDS = ['tags'']
RELATED_FIELDS = ['coordinate']
# I omit fields and Meta declare for this example
class TravelRetrieveSerializer(QuerySerializerMixin, serializers.ModelSerializer):
PREFETCH_FIELDS = ['tags', 'route_places']
Now rewrite our ViewSet with new serializers
class TravelViewset(viewsets.ModelViewSet):
queryset = Travel.objects.all()
def get_serializer_class():
if self.action == 'retrieve':
return TravelRetrieveSerializer
elif self.action == 'list':
return TravelListSerializer
else:
return SomeDefaultSerializer
def get_queryset(self):
# This method return serializer class
# which we pass in class method of serializer class
# which is also return by get_serializer()
q = super(TravelViewset, self).get_queryset()
serializer = self.get_serializer()
return serializer.get_related_queries(q)