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

Django DRF查询符合Child筛选条件的Parent时如何附加匹配ChildID且不重复查询数据库

实现方案

完全可以通过Django内置聚合函数配合DRF序列化器实现该需求,全程仅执行1次数据库查询,无额外访问开销,比原有实现的查询效率更高。

前置依赖

  • Django 3.2+(PostgreSQL数据库可支持更早版本),如果使用MySQL需Django 4.1+ + MySQL 8.0+
  • 无需安装额外第三方依赖

步骤1:改造查询集

使用Django内置的ArrayAgg聚合函数,直接在Parent查询时注解出匹配的Child ID数组,同时完成过滤逻辑:

from django.db.models import ArrayAgg, Q
from rest_framework import generics

# 其他导入保持不变

class ParentSearchByChildNameView(generics.ListAPIView):
    """
    返回所有名下存在符合名称搜索条件子节点的父节点列表,附带匹配的子节点ID数组
    """
    serializer_class = ParentSerializer

    def get_queryset(self):
        child_name = self.request.query_params.get('name')
        queryset = Parent.objects.all()
        
        if child_name is not None:
            queryset = queryset.annotate(
                # 聚合所有符合名称条件的子节点ID为数组
                matched_child=ArrayAgg(
                    'children__id',
                    filter=Q(children__name__contains=child_name),
                    distinct=True  # 避免重复ID,可根据业务需要取舍
                )
            # 过滤掉没有匹配子节点的父节点,和原有逻辑保持一致
            ).filter(matched_child__isnull=False)
        
        return queryset

步骤2:修改序列化器

在ParentSerializer中新增对应注解字段的读取配置即可:

from rest_framework import serializers
# 导入Parent模型

class ParentSerializer(serializers.ModelSerializer):
    # 直接读取查询集注解出来的数组字段
    matched_child = serializers.ListField(child=serializers.IntegerField(), read_only=True)
    
    class Meta:
        model = Parent
        fields = ('url', 'name', 'matched_child')

方案说明

该方案的所有逻辑都在单次SQL查询中完成,聚合、过滤操作均在数据库层面执行,不会产生后续的N+1查询或者额外数据库访问,性能远高于先查子节点再遍历填充父节点字段的实现。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 21:45:03