加速序列化程序查询

假设我们的模型 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()

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

class TravelViewset(viewsets.ModelViewSet):

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

这个 ViewSet 的问题是我们的 Travel 模型中有很多相关的字段,所以 Django 会为每个 Travel 实例命中 db。我们可以直接在 queryset 属性中调用 select_related 和 prefetch_related ,但是如果我们想要为 Viewh 的 listretrievecreate ..动作分隔序列化器呢?
所以我们可以将这个逻辑放在一个 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():
        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)