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

Django按年份分组日期构建时间线(DRF返回前端)

解决方案:基于DRF实现按年份分组的产品时间线

1. 核心查询逻辑(解决annotate/values顺序问题)

之前用annotate和values没得到预期结果,大概率是查询顺序错误:需要先通过函数提取年份,再按年份分组,最后聚合统计。推荐两种实现方式:

方式一:用TruncYear截断日期到年份(返回日期对象,便于格式转换)

from django.db.models.functions import TruncYear
from django.db.models import Count

# 按年份分组,统计每年的产品数量
grouped_data = ProductModel.objects.annotate(
    year=TruncYear('created')  # 将created字段截断到年份,生成year字段
).values('year').annotate(
    product_count=Count('id')  # 统计每年的产品数量
).order_by('year')  # 按年份升序排序

方式二:用ExtractYear直接提取年份整数

from django.db.models.functions import ExtractYear
from django.db.models import Count

grouped_data = ProductModel.objects.annotate(
    year=ExtractYear('created')  # 直接提取年份整数
).values('year').annotate(
    product_count=Count('id')
).order_by('year')

2. 转换为前端需要的格式

将查询结果转换为易处理的字典列表:

# 针对TruncYear的结果,将日期对象转为YYYY格式字符串
timeline_data = [
    {
        "year": item['year'].strftime("%Y"),
        "count": item['product_count']
    } for item in grouped_data
]

# 针对ExtractYear的结果,直接转字符串即可
timeline_data = [
    {
        "year": str(item['year']),
        "count": item['product_count']
    } for item in grouped_data
]

如果前端只需要纯年份列表(比如["2021", "2022"]),可简化为:

timeline_years = [str(item['year']) for item in grouped_data]  # ExtractYear场景
# 或
timeline_years = [item['year'].strftime("%Y") for item in grouped_data]  # TruncYear场景

3. 序列化器实现(使用ListField)

根据返回格式选择对应的序列化器:

情况一:返回带统计的分组列表

from rest_framework import serializers

# 单个年份项的序列化器
class YearItemSerializer(serializers.Serializer):
    year = serializers.CharField()
    count = serializers.IntegerField()

# 外层包含ListField的序列化器
class TimelineSerializer(serializers.Serializer):
    timeline = serializers.ListField(child=YearItemSerializer())

情况二:返回纯年份列表

直接用ListField嵌套字符字段即可:

from rest_framework import serializers

class TimelineSerializer(serializers.Serializer):
    timeline = serializers.ListField(child=serializers.CharField())

4. 视图层实现(APIView示例)

from rest_framework.views import APIView
from rest_framework.response import Response

class ProductTimelineView(APIView):
    def get(self, request):
        # 执行分组查询(以ExtractYear为例)
        grouped_data = ProductModel.objects.annotate(
            year=ExtractYear('created')
        ).values('year').annotate(
            product_count=Count('id')
        ).order_by('year')
        
        # 转换数据格式
        timeline_data = [
            {"year": str(item['year']), "count": item['product_count']} 
            for item in grouped_data
        ]
        
        # 序列化并返回
        serializer = TimelineSerializer(data={"timeline": timeline_data})
        serializer.is_valid(raise_exception=True)
        return Response(serializer.data)

预期返回结果

当有3个2021年、3个2022年产品时,返回格式如下:

{
    "timeline": [
        {"year": "2021", "count": 3},
        {"year": "2022", "count": 3}
    ]
}

如果是纯年份列表,返回:

{
    "timeline": ["2021", "2022"]
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 16:50:21