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

Django DRF使用PrimaryKeyRelatedField时overfetching问题如何解决

DRF PrimaryKeyRelatedField 多对多关联过取数据优化方案

你遇到的过取问题根源是prefetch_related("characters")默认会查询关联Character模型的全部字段,而序列化仅需要id字段,两种优化方案如下:

方案1:最小改动优化(兼容性最好)

自定义Prefetch对象,指定关联查询仅拉取id字段,无需修改序列化器原有逻辑:

# 视图文件新增导入
from django.db.models import Prefetch

class QuestController(viewsets.ModelViewSet):
    serializer_class = QuestSerializer

    def get_queryset(self):
        return Quest.objects.filter(campaign_id=self.kwargs["campaign_id"]).prefetch_related(
            Prefetch("characters", queryset=Character.objects.only("id"))
        )
        
    def perform_create(self, serializer):
        return serializer.save(campaign_id=self.kwargs["campaign_id"])

优化后关联查询仅会取Character表的id字段,大幅减少冗余数据拉取量。

方案2:最优性能方案(完全跳过Character表)

直接通过聚合函数从多对多中间表拉取id数组,不需要关联查询Character表,完全符合你预期的SQL逻辑:

# 视图文件新增导入(PostgreSQL环境,其他数据库可替换为对应聚合函数)
from django.db.models import ArrayAgg

class QuestController(viewsets.ModelViewSet):
    serializer_class = QuestSerializer

    def get_queryset(self):
        return Quest.objects.filter(campaign_id=self.kwargs["campaign_id"]).annotate(
            character_ids=ArrayAgg("characters__id")
        )
        
    def perform_create(self, serializer):
        return serializer.save(campaign_id=self.kwargs["campaign_id"])

同步修改序列化器,直接读取聚合好的id数组:

class QuestSerializer(serializers.ModelSerializer):
    characters = serializers.ListField(
        child=serializers.IntegerField(), 
        source="character_ids", 
        read_only=True
    )

    class Meta:
        model = Quest
        fields = "__all__"

如果使用MySQL等不支持ArrayAgg的数据库,可选用第一种方案,兼容性更强。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 03:39:01