DRF如何按模型id字段分组序列化数据并聚合客户端ID
DRF按Mailing分组聚合Client ID数组的最优方案
直接用数据库层面的聚合函数实现,彻底避免SerializerMethodField带来的N+1查询问题,分数据库类型给出具体实现:
一、PostgreSQL环境(推荐,原生支持数组聚合)
1. 调整查询集(views.py)
导入PostgreSQL专属的ArrayAgg,在annotate中聚合关联的Client ID:
from django.contrib.postgres.aggregates import ArrayAgg from .models import Mailing from rest_framework import generics class MailingListView(generics.ListAPIView): serializer_class = MailingSerializer def get_queryset(self): # 按Mailing的id分组,聚合去重后的Client ID数组 return Mailing.objects.annotate( client_ids=ArrayAgg('client__id', distinct=True) # 注意:'client__id'需匹配你的模型关联字段,比如多对多可能是'clients__id' ).values('id', 'client_ids', '其他需要的字段') # 只查询必要字段,提升性能
2. 修改序列化器(serializers.py)
直接映射聚合后的client_ids字段为数组:
from rest_framework import serializers from .models import Mailing class MailingSerializer(serializers.ModelSerializer): clients = serializers.ListField(child=serializers.IntegerField(), source='client_ids') class Meta: model = Mailing fields = ['id', 'clients', '其他需要的字段']
二、MySQL环境(需自定义聚合函数)
MySQL原生无ArrayAgg,用GROUP_CONCAT实现字符串聚合后转数组:
1. 自定义聚合函数(可放在models.py或单独的utils文件)
from django.db.models import Func from django.db.models.fields import CharField class GroupConcat(Func): function = 'GROUP_CONCAT' template = "%(function)s(%(expressions)s SEPARATOR ',')" output_field = CharField()
2. 调整查询集(views.py)
from .models import Mailing from .utils import GroupConcat # 或自定义函数的实际路径 from rest_framework import generics class MailingListView(generics.ListAPIView): serializer_class = MailingSerializer def get_queryset(self): return Mailing.objects.annotate( client_ids_str=GroupConcat('client__id', distinct=True) ).values('id', 'client_ids_str', '其他需要的字段')
3. 修改序列化器(serializers.py)
将聚合得到的字符串转为整数数组:
from rest_framework import serializers from .models import Mailing class MailingSerializer(serializers.ModelSerializer): clients = serializers.SerializerMethodField() def get_clients(self, obj): # 处理空值情况,避免报错 client_str = obj.get('client_ids_str') return list(map(int, client_str.split(','))) if client_str else [] class Meta: model = Mailing fields = ['id', 'clients', '其他需要的字段']
三、排查之前ArrayAgg失败的常见原因
- 数据库不是PostgreSQL:MySQL不支持
ArrayAgg,需用上述MySQL方案 - 关联字段路径错误:比如模型中Mailing与Client是多对多,关联字段名为
clients,则应写clients__id而非client__id - 未加
distinct=True:如果存在重复关联,会导致数组中出现重复ID
效果验证
之前的输出(单条Client ID):
[ {"id": 1, "client": 123}, {"id": 1, "client": 456} ]
优化后的期望输出:
[ {"id": 1, "clients": [123, 456]} ]
内容的提问来源于stack exchange,提问作者aleksandrkomyagin
相关产品推荐
相关产品推荐

