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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 22:17:36