You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Django ViewSet数据库查询优化求助:反向关联下prefetch_related失效

优化DRF反向关联与深层多对多查询的性能方案

问题根源

你当前的核心问题有两个:

  1. 反向关联的collection_set在序列化阶段才做过滤(FilteredCollectionSerializer),导致无法直接用普通prefetch_related预取有效数据,还会加载大量无用的Collection记录。
  2. 深层关联的card__tag、card__system_tags以及collection__system_tags都没有预取,触发了大量N+1查询。

解决方案:嵌套Prefetch预取所有关联数据

通过Django的Prefetch对象,我们可以在数据库层面完成反向关联的过滤,同时嵌套预取所有深层关联的多对多数据,彻底消除额外查询。

1. 修改ViewSet的查询集

在SectionViewset的get_queryset中,使用嵌套的Prefetch一次性预取所有需要的数据:

from django.db.models import Prefetch
from datetime import datetime

class SectionViewset(viewsets.ModelViewSet):
    serializer_class = serializers.SectionSerializer
    http_method_names = ['get']

    def get_queryset(self):
        now = datetime.now()
        
        # 1. 定义过滤后的Collection查询集,同时预取其关联的card、system_tags
        filtered_collections = Collection.objects.filter(
            collection_status=True,
            collection_expiredate__gte=now
        ).prefetch_related(
            # 2. 预取Card,并同时预取Card的tag和system_tags
            Prefetch(
                'card',
                queryset=Card.objects.prefetch_related('tag', 'system_tags')
            ),
            # 3. 预取Collection自身的system_tags
            'system_tags'
        )
        
        # 4. 预取Section关联的过滤后的Collection集合
        queryset = Section.objects.filter(
            section_status=True,
            section_expiredate__gte=now
        ).prefetch_related(
            Prefetch('collection_set', queryset=filtered_collections)
        )
        return queryset

2. 可选:调整Serializer的过滤逻辑

由于已经在数据库层面完成了Collection的过滤,你可以移除FilteredCollectionSerializer,简化CollectionSerializer的配置:

class CollectionSerializer(serializers.ModelSerializer):
    card = CardSerializer(many=True, read_only=True)
    system_tags = AssetTagBadgeSerializer(many=True, read_only=True)
    
    class Meta:
        model = Collection
        fields = ('id', 'card', 'system_tags')

如果必须保留序列化阶段的过滤(比如有额外的业务逻辑),也可以继续使用FilteredCollectionSerializer,此时它只会过滤已经预取的有效数据,不会触发新的数据库查询。

优化原理

  • 数据库层面过滤:通过Prefetch指定反向关联的查询集,避免加载无用的Collection记录,减少数据传输和内存占用。
  • 嵌套预取:一次性预取所有层级的关联数据(Section→Collection→Card→Tag/SystemTag、Collection→SystemTag),将原本的N+1查询缩减为固定的几次查询。

内容的提问来源于stack exchange,提问作者ichthyocentaurs

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.28 05:35:13