加速序列化程式查詢

假設我們的模型 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)