可以分别按多个列进行分组,并使用Django ORM将其中的每一列聚合到另一列?

2024-09-28 19:00:50 发布

您现在位置:Python中文网/ 问答频道 /正文

我知道如何GROUP BY和聚合:

>>> from expenses.models import Expense
>>> from django.db.models import Sum
>>> qs = Expense.objects.order_by().values("is_fixed").annotate(is_fixed_total=Sum("price"))
>>> qs
<ExpenseQueryset [{'is_fixed': False, 'is_fixed_total': Decimal('1121.74000000000')}, {'is_fixed': True, 'is_fixed_total': Decimal('813.880000000000')}]>

但是,如果我想对其他两列执行相同的操作,它只返回最后一列:

>>> qs = (
...     Expense.objects.order_by()
...     .values("is_fixed")
...     .annotate(is_fixed_total=Sum("price"))
...     .values("source")
...     .annotate(source_total=Sum("price"))
...     .values("category")
...     .annotate(category_total=Sum("price"))
... )
>>> qs
<ExpenseQueryset [{'category': 'FOOD', 'category_total': Decimal('33.9000000000000')}, {'category': 'GIFT', 'category_total': Decimal('628')}, {'category': 'HOUSE', 'category_total': Decimal('813.880000000000')}, {'category': 'OTHER', 'category_total': Decimal('307')}, {'category': 'RECREATION', 'category_total': Decimal('100')}, {'category': 'SUPERMARKET', 'category_total': Decimal('52.8400000000000')}]>

用一个而不是三个查询就可以完成我想要的吗

预期结果:

<ExpenseQueryset [{'category': 'FOOD', 'total': Decimal('33.9000000000000')}, {... all other categories ...}, 
{'source': 'MONEY', 'total': Decimal('100')}, {... all other sources ...}, {'is_fixed': False, 'total': Decimal('1121.74000000000')}, {'is_fixed': True, 'total': Decimal('813.880000000000')}]>

最理想的情况是,它可以分为以下几部分:

<ExpenseQueryset ['categories': [{'category': 'FOOD', 'total': Decimal('33.9000000000000')}, {... all other categories ...}], 
'sources': [{'source': 'MONEY', 'total': Decimal('100')}, {... all other sources ...}], 'type': [{'is_fixed': False, 'total': Decimal('1121.74000000000')}, {'is_fixed': True, 'total': Decimal('813.880000000000')}]]>

但这只是一个很大的优点


Tags: sourceisallpricetotalfixeddecimalvalues
1条回答
网友
1楼 · 发布于 2024-09-28 19:00:50

答案是否定的,因为使用SQL是不可能的

但是您可以将以下方法与python编码结合使用:

我认为即使在原始SQL中也不可能,因为在每个查询中,您可以通过一个或多个字段将其分组在一起,但不能对每个字段使用单独的结果。 但是只需一个查询,就可以使用少量python代码以您想要的格式合并结果。下面我描述了如何一步一步地使用它。在下一节中,我们编写了一个python方法,您可以动态地使用该方法进行任何进一步的使用

工作原理

我能提到的唯一一个简单的解决方案是,按照您想要的3个字段进行分组,然后执行一个简单的python编程,将每个字段的结果相加。 在这种方法中,u将只有一个查询,但每个字段组的结果是分开的

from expenses.models import Expense
from django.db.models import Sum

qs = Expense.objects.order_by().values("is_fixed", "source", "category").annotate(total=Sum("price"))

现在,结果如下所示:

<ExpenseQueryset [{'category': 'FOOD', 'is_fixed': False, 'source': 'MONEY', 'total': Decimal('33.9000000000000')}, { ...}, 

现在,我们可以通过迭代这个结果来简单地聚合每个字段结果

category_keys = []
for q in qs:
    if not q['category'] in category_keys:
        category_keys.append(q['category'])

# Now we have proper values of category in category_keys
category_result = []
for c in category_keys:
    value = sum(item['total'] for item in qs if item['category'] == c)
    category_result.append({'category': c, 'total': value)

category字段的结果如下:

[{'category': 'FOOD', 'total': 33.3}, {... other category results ...}

现在我们可以继续并为其他group by字段is_fixedsource生成结果,如下所示:

source_keys = []
for q in qs:
    if not q['source'] in source_keys:
        source_keys.append(q['source'])
source_result = []
for c in source_keys:
    value = sum(item['total'] for item in qs if item['source'] == c)
    source_result.append({'source': c, 'total': value)

is_fixed_keys = []
for q in qs:
    if not q['is_fixed'] in is_fixed_keys:
        source_keys.append(q['is_fixed'])
is_fixed_result = []
for c in is_fixed_keys:
    value = sum(item['total'] for item in qs if item['is_fixed'] == c)
    is_fixed_result.append({'is_fixed': c, 'total': value)

全局解

现在我们知道了如何使用这个解决方案,下面是一个函数,它只提供您想要的字段,并动态地为您生成适当的结果

def find_group_by_separated_by_keys(key_list):
    """ key_list in this example will be:
        key_list = ['category', 'source', 'is_fixed']
    """
    qs = Expense.objects.order_by().values(*tuple(key_list)).annotate(total=Sum("price"))
    qs = list(qs)
    result = []
    for key in key_list:
        key_values = []
        for item in qs:
            if not item[key] in key_values:
                key_values.append(item[key])
        
        key_result = []
        for v in key_values:
            value = sum(item['total'] for item in qs if item[key] == v)
            key_result.append({key: v, 'total': value})

        result.extend(key_result)
    return result

现在只需简单地在您的代码中使用它,如下所示:

find_group_by_separated_by_keys(['category', 'source', 'is_fixed')

它会给出一个值列表,比如你想要的正确格式

相关问题 更多 >