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
相关产品推荐
相关产品推荐

