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

Python如何分组年月列表去重年份 格式化Django年月查询结果

问题背景

我有一张存储订单的数据表,表中包含date日期字段,需要从该日期字段聚合提取年份、月份数据,用于前端筛选器使用。目前已经成功查询到对应数据,但返回格式不符合预期。

现有查询代码(Python/Django ORM)

years = purchase_orders.objects.filter(user_id=info.context.user.id).annotate(
    year=ExtractYear('date'), 
    month=ExtractMonth('date'),
).order_by().values_list('year', 'month').order_by('year', 'month').distinct()

当前返回数据

<QuerySet [(2020, 3), (2021, 4), (2022, 1), (2022, 2), (2022, 3), (2022, 4), (2022, 5)]>

期望输出格式

<QuerySet [(2020, (3)), (2021, (4)), (2022, (1, 2, 3, 4, 5))]>

核心需求:将查询返回的(年份,月份)元组列表按年份分组,去除重复年份条目,将同一年份下的所有月份聚合为嵌套元组,得到符合期望格式的结果。


解决方案

Django原生QuerySet不直接支持元素为嵌套元组的返回结构,你可以根据数据量大小选择以下两种实现方式:

方案1:内存分组(推荐,绝大多数场景适用)

你当前的查询已经完成了年、月维度的去重和排序,直接用Python标准库的itertools.groupby做分组即可,没有额外数据库开销,代码简洁:

from itertools import groupby

# 保留原有查询逻辑,拿到排序去重后的年月数据
raw_ym_data = purchase_orders.objects.filter(
    user_id=info.context.user.id
).annotate(
    year=ExtractYear('date'), 
    month=ExtractMonth('date'),
).order_by().values_list('year', 'month').order_by('year', 'month').distinct()

# 按年份分组,组装目标结构
formatted_result = []
for year, month_group in groupby(raw_ym_data, key=lambda x: x[0]):
    # 提取同一年份下的所有月份,转为元组
    month_tuple = tuple(item[1] for item in month_group)
    formatted_result.append((year, month_tuple))

执行后formatted_result的结构为:

[(2020, (3,)), (2021, (4,)), (2022, (1, 2, 3, 4, 5))]

注意:Python语法中单个元素的元组必须保留尾部逗号,即(3,),示例中写的(3)会被识别为整数而非元组,前端对接时注意区分类型。
如果你不需要严格的QuerySet类型,直接使用这个列表即可,完全满足前端筛选器的数据格式要求。

方案2:数据库层聚合(适合百万级以上大表场景)

如果订单表数据量极大,不想把所有年月明细拉到内存处理,可以用数据库的数组聚合函数直接在查询层完成分组:
以PostgreSQL数据库为例,使用Django内置的ArrayAgg聚合器:

from django.contrib.postgres.aggregates import ArrayAgg

raw_grouped_data = purchase_orders.objects.filter(
    user_id=info.context.user.id
).annotate(
    year=ExtractYear('date')
).values('year').annotate(
    # 按年分组,聚合对应月份,自动去重、按月份排序
    month_list=ArrayAgg(
        ExtractMonth('date'), 
        distinct=True, 
        ordering='month_list'
    )
).order_by('year').values_list('year', 'month_list')

# 将数据库返回的月份列表转为元组,匹配目标格式
formatted_result = [(year, tuple(months)) for year, months in raw_grouped_data]

如果使用MySQL数据库,可以替换ArrayAgg为GROUP_CONCAT聚合函数,拿到逗号分隔的月份字符串后,切割转换为整数元组即可。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 14:36:30