Django中合并同名多QuerySet并实现数据扁平化的方案
问题描述
我有多个结构类似的QuerySet,比如:
NAME1 | VALUE_FROM_COLUMN_1 NAME1 | VALUE_FROM_COLUMN_2 NAME1 | VALUE_FROM_COLUMN_3
使用list(chain(q1, q2, q3))得到的结果不符合预期:
NAME1 | VALUE_FROM_COLUMN_1 | | NAME1 | | VALUE_FROM_COLUMN_2 | NAME1 | | | VALUE_FROM_COLUMN_3
我期望的扁平化结果是:
NAME1 | VALUE_FROM_COLUMN_1 | VALUE_FROM_COLUMN_2 | VALUE_FROM_COLUMN_3
具体场景
我的模型定义如下:
class Order(models.Model): ord_id = models.AutoField(unique=True, primary_key=True) ord_data_real = models.DateField() class OrderElement(models.Model): ordel_id = models.AutoField(unique=True, primary_key=True) ordel_order_fk = models.ForeignKey(Order, on_delete=models.CASCADE) ordel_name = models.CharField(max_length=100, unique=False) ordel_qty = models.IntegerField(unique=False)
我通过两个查询分别统计不同时间段的订单元素总量:
list1 = OrderElement.objects.filter( ordel_order_fk_id__in=Order.objects.filter( ord_status=2, ord_data_real__lte=datetime.now() + timedelta(days=7) ).values_list('ord_id', flat=True) ).values('ordel_name').annotate(total1=Sum('ordel_qty')).order_by() list2 = OrderElement.objects.filter( ordel_order_fk_id__in=Order.objects.filter( ord_status=2, ord_data_real__lte=datetime.now() + timedelta(days=14), ord_data_real__gte=datetime.now() + timedelta(days=7) ).values_list('ord_id', flat=True) ).values('ordel_name').annotate(total2=Sum('ordel_qty')).order_by()
得到的结果示例:
- list1:
ordel_name: OrderElementName1, total1: 50 ... - list2:
ordel_name: OrderElementName1, total2: 100 ...
需要把两个QuerySet中同ordel_name的数据合并到同一行,实现扁平化展示。
解决方案
方法1:Python层面合并(适合小数据量场景)
将两个QuerySet转为字典,以ordel_name为键,再合并字段:
from itertools import chain # 转换为字典,键为ordel_name,值为对应统计值 dict1 = {item['ordel_name']: item['total1'] for item in list1} dict2 = {item['ordel_name']: item['total2'] for item in list2} # 获取所有唯一的订单元素名称 all_names = set(dict1.keys()).union(set(dict2.keys())) # 合并结果 merged_result = [] for name in all_names: merged_result.append({ 'ordel_name': name, 'total1': dict1.get(name, 0), 'total2': dict2.get(name, 0) })
方法2:数据库层面JOIN(推荐,性能更优)
直接通过一次查询完成多时间段统计,用Case+When按日期条件分组求和,避免多次查询后再合并:
from django.db.models import Sum, Case, When, Q from datetime import datetime, timedelta now = datetime.now() result = OrderElement.objects.filter( ordel_order_fk__ord_status=2, ordel_order_fk__ord_data_real__lte=now + timedelta(days=14) ).values('ordel_name').annotate( total1=Sum(Case( When(ordel_order_fk__ord_data_real__lte=now + timedelta(days=7), then='ordel_qty'), default=0, output_field=models.IntegerField() )), total2=Sum(Case( When(Q(ordel_order_fk__ord_data_real__gte=now + timedelta(days=7)) & Q(ordel_order_fk__ord_data_real__lte=now + timedelta(days=14)), then='ordel_qty'), default=0, output_field=models.IntegerField() )) ).order_by()
该查询会直接返回每个ordel_name对应的两个时间段总量,一步得到扁平化结果,数据量大时性能远优于Python层面合并。
内容的提问来源于stack exchange,提问作者Jorhanc
相关产品推荐
相关产品推荐

