如何使用Django QuerySet与Serializer按年月排序数据并生成指定格式的订单分组响应
嘿,作为Django REST Framework的新手,这个按年月分组序列化的需求其实不难,我一步步给你讲清楚怎么实现:
解决方案:按年月分组并序列化Order数据
1. 先搞定QuerySet的排序与分组准备
首先,我们需要从数据库里把Order数据取出来,并且按创建时间排序——这很重要,因为后面分组的时候需要相同月份的订单是连续的:
# 获取所有订单,按创建时间降序排列(最新的订单在前) orders_queryset = Order.objects.all().order_by('-created_at')
如果你想先看看每个月有多少订单,可以用Django的数据库函数TruncMonth来做统计(这一步可选,主要是辅助了解数据):
from django.db.models import Count, TruncMonth monthly_order_counts = Order.objects.annotate( month=TruncMonth('created_at') ).values('month').annotate( total_orders=Count('id') ).order_by('-month')
但要获取每个月的完整订单列表,我们更多会在Python代码层面用itertools.groupby来分组,因为QuerySet没法直接返回嵌套的订单列表结构。
2. 定义匹配JSON结构的Serializer
我们需要三层Serializer来对应你要的JSON格式:
2.1 订单子项Serializer
先写一个只序列化订单id的简单Serializer:
from rest_framework import serializers from .models import Order class OrderItemSerializer(serializers.ModelSerializer): class Meta: model = Order fields = ('id',)
2.2 月份分组Serializer
然后是每个月份分组的Serializer,包含格式化后的月份名称和对应的订单列表:
class OrderMonthSerializer(serializers.Serializer): month = serializers.CharField() order_list = OrderItemSerializer(many=True)
2.3 顶层Serializer
最后是包裹整个数组的顶层Serializer:
class MonthlyOrderListSerializer(serializers.Serializer): order_by_month_year = OrderMonthSerializer(many=True)
3. 在视图里完成分组与序列化
接下来在视图中把QuerySet的数据按年月分组,再传给Serializer输出:
from rest_framework.views import APIView from rest_framework.response import Response import itertools from django.utils.dateformat import DateFormat class MonthlyOrderView(APIView): def get(self, request): # 获取排序后的订单数据 orders = Order.objects.all().order_by('-created_at') # 定义分组的key函数:把created_at转成"January 2021"这样的格式 def month_group_key(order): date_formatter = DateFormat(order.created_at) return date_formatter.format('F Y') # F是完整月份名,Y是4位年份 # 按月份分组(注意:groupby要求数据已按key排序,所以前面的order_by必须有) grouped_data = [] for month_name, order_group in itertools.groupby(orders, key=month_group_key): grouped_data.append({ 'month': month_name, 'order_list': list(order_group) }) # 用顶层Serializer序列化数据 serializer = MonthlyOrderListSerializer(data={'order_by_month_year': grouped_data}) serializer.is_valid(raise_exception=True) return Response(serializer.data)
几个关键注意点
- 排序不能少:
itertools.groupby只会把连续的相同key的元素分到一组,所以必须先让QuerySet按created_at排序,确保同一个月份的订单挨在一起。 - 日期格式化要准确:用Django自带的
DateFormat比Python原生的strftime更好,因为它会适配你项目的本地化设置(比如如果是中文环境,会自动显示“2021年1月”)。 - Serializer的选择:因为我们的输出结构不是直接对应单个模型,所以顶层和月份分组的Serializer用自定义的
serializers.Serializer,而订单子项用ModelSerializer更省心。
测试效果
启动Django服务后,访问这个视图的URL,就能得到你想要的JSON结构啦!
内容的提问来源于stack exchange,提问作者Shakeel Afridi
相关产品推荐
相关产品推荐

