django-rest-framework 加快序列化程序查询

示例

假设我们有Travel很多相关领域的模型:

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()

我们想/travels通过ViewViewSet构建CRUD 。
这是简单的视图集:

class TravelViewset(viewsets.ModelViewSet):

    queryset = Travel.objects.all()
    serializer_class = TravelSerializer

该ViewSet的问题在于我们的Travel模型中有许多相关字段,因此Django将为每个Travel实例命中db 。我们可以称之为select_related并直接prefetch_relatedqueryset属性,但如果我们想单独为串行什么list,retrieve,create...视图集中行动。
因此,我们可以将此逻辑放入一个mixin并从中继承:

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']

现在ViewSet用新的序列化器重写我们的

class TravelViewset(viewsets.ModelViewSet):

    queryset = Travel.objects.all()
        
    def get_serializer_class():
        ifself.action== 'retrieve':
            return TravelRetrieveSerializer
        elifself.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)