Django ORM如何实现分类难度总和Top3及其余项汇总QuerySet
Django 任务分类聚合合并尾部排名实现方案
不需要构造复杂度极高的多层ORM嵌套或者原生SQL,拆分逻辑做内存处理即可,代码易读易维护,适配所有Django支持的数据库,完全匹配你需要的返回结构。
核心实现逻辑
- 第一步:复用你已写的聚合逻辑,拿到按
difficult总和降序排列的全部分类统计结果,转成列表方便切片处理 - 第二步:切分列表取前3名作为结果的前半部分
- 第三步:累加第3名之后所有分类的
difficult总和,如果存在剩余分类,追加category__name为Anothers的记录到结果末尾
可直接运行的代码
from django.db.models import Sum, DecimalField from django.db.models.functions import Coalesce from decimal import Decimal # 1. 查询所有分类的difficult聚合结果,按总和降序排列 category_agg = list( Task.objects.values("category__name") .annotate( category_sum=Coalesce( Sum("difficult"), Decimal("0.00"), output_field=DecimalField(max_digits=10, decimal_places=2), ) ) .filter(category__isnull=False) # 实际业务使用记得补充user过滤,比如.filter(category__isnull=False, user=当前登录用户),避免跨用户数据统计 .order_by("-category_sum") ) # 2. 拆分Top3分类和剩余分类 top3_categories = category_agg[:3] remaining_categories = category_agg[3:] final_result = top3_categories # 3. 存在剩余分类时计算总和,追加Anothers记录 if remaining_categories: others_total = sum(item["category_sum"] for item in remaining_categories) final_result.append( { "category__name": "Anothers", "category_sum": others_total.quantize(Decimal("0.00")), # 保持和字段一致的两位小数格式 } )
final_result就是你需要的最终返回结构。
补充说明
- 原聚合查询没有加
user字段过滤,会统计全平台所有用户的任务数据,实际业务使用时记得在filter()中补充对应用户的过滤规则,避免数据越权 - 上述方案的性能开销极低:聚合后的结果集大小等于系统内的任务分类总数,绝大多数场景下分类总量在百级以内,内存切片求和的开销可以忽略,且不存在数据库版本兼容问题
- 如果分类总数小于等于3,代码不会追加Anothers记录,符合常规业务逻辑
- 如果你遇到分类总量超十万级的极端场景,才需要考虑用窗口函数在数据库层做合并,该方案要求MySQL 8.0+、PostgreSQL等支持窗口函数的数据库,写法复杂度高且排查问题难度大,非必要不推荐使用
内容的提问来源于stack exchange,提问作者Seofale
相关产品推荐
相关产品推荐

