【问题标题】:Django: Problem in doing complex annotation and aggregationDjango:进行复杂注释和聚合的问题
【发布时间】:2019-06-06 02:51:05
【问题描述】:

这是模型:

class Purchase(models.Model):
    date           = models.DateField(default=datetime.date.today,blank=False, null=True)
    total_purchase = models.DecimalField(max_digits=10,decimal_places=2,blank=True, null=True)

我想在特定日期范围内按月计算“total_purchase”,这样如果一个月内没有购买,总购买应该是上个月的购买价值 如果两个月内有购买,那么总购买量将加上这两个......

示例:

假设用户给出的日期范围是从四月到十一月。

如果在 4 月购买了 2800 美元,在 8 月购买了 5000 美元,在 10 月购买了 6000 美元。

那么输出会是这样的:

April      2800
May        2800
June       2800
July       2800
August     7800  #(2800 + 5000)
September  7800
October    13800 #(7800 + 6000)
November   13800

知道如何在 django 查询中执行此操作吗?

谢谢

根据Mr.Raydel Miranda 给出的答案。我做了以下

import calendar
import collections
import dateutil

start_date = datetime.date(2018, 4, 1)
end_date = datetime.date(2019, 3, 31)

results = collections.OrderedDict()

result = Purchase.objects.filter(date__gte=start_date, date__lt=end_date).annotate(real_total = Case(When(Total_Purchase__isnull=True, then=0),default=F('tal_Purchase')))

date_cursor = start_date

while date_cursor < end_date:
    month_partial_total = result.filter(date__month=date_cursor.month).agggate(partial_total=Sum('real_total'))['partial_total']

    results[date_cursor.month] = month_partial_total

    if month_partial_total == None:
            month_partial_total = int(0)
    else:
            month_partial_total = month_partial_total

    date_cursor += dateutil.relativedelta.relativedelta(months=1)

    return results

但是现在输出是这样的(来自上面的示例):

April      2800
May        0
June       0
July       0
August     5000
September  0
October    6000
November   0

有没有人知道如何在月份之间添加... 我想做类似的事情

e = month_partial_total + month_partial_total.next

我想添加每个month_partial_total的下一次迭代值。我认为这将解决我的问题..

有人知道如何在 django 中执行此操作吗?

谢谢

【问题讨论】:

  • 您需要一个window function 来计算按月分组的运行总计。如果我稍后有时间,我会尝试为您制定详细信息。
  • 其实我是 Django 的新手...如果您能帮我解决详细信息将会非常有帮助,因为我不知道这个窗口函数在应用程序中的实现...我是只是通过基础知识...
  • 你不能循环数月(同时固定初始值)聚合值吗?类似于Purchase.objects.filter(date__range=(START_DATE,FIRST_MONTH_END_DATE).aggregate(total=Sum('total_purchase')) 的内容,然后将您的FIRST_MONTH_END_DATE 提前1 个月,依此类推,直到您到达指定时间范围的末尾?但不确定它的效率如何。
  • 这将不是执行上述解决方案的便捷方式,因为日期范围将由用户选择,该日期范围将具有开始日期和结束日期......我无法提取最后一天当月

标签: django django-models django-aggregation django-annotate


【解决方案1】:

解决方案

根据Mr.Raydel Miranda给出的答案,我终于找到了解决问题的办法……

在我的观点中我做了以下事情,效果很好:

import datetime
import calendar
import collections
import dateutil
start_date = datetime.date(2018, 4, 1)
end_date = datetime.date(2019, 3, 31)
results = collections.OrderedDict()
result = Purchase.objects.filter(date__gte=start_date, date__lt=end_date).annotate(real_total = Case(When(Total_Purchase__isnull=True, then=0),default=F('Total_Purchase')))
date_cursor = start_date
z = 0
while date_cursor < end_date:
    month_partial_total = result.filter(date__month=date_cursor.month).aggregate(partial_total=Sum('real_total'))['partial_total']
    # results[date_cursor.month] = month_partial_total
    if month_partial_total == None:
        month_partial_total = int(0)
        e = month_partial_total
    else:
        e = month_partial_total

    z = z + e

    results[date_cursor.month] = z

    date_cursor += dateutil.relativedelta.relativedelta(months=1)

 return results

谢谢大家。

【讨论】:

  • 老兄,这是我的答案,修改了一些变量名!!
  • 您是否复制并更改了我的答案以避免失去您提供的奖励积分?
  • 我并不是要不尊重你 Mr.Raydel Miranda...我刚刚发布了我上面给出的问题的确切解决方案,只是看到不仅有一些可变的变化,而且我还添加了一些额外的逻辑也为上述整个问题提供了确切的解决方案......
  • 是的,但是您要求的复杂注释或聚合是我提供的,对吧?
  • 那当然是正确的...我刚刚发布了确切的解决方案...对不起,如果我不尊重您...您在很多方面帮助了我...我非常感谢您...
【解决方案2】:

我在您的问题中指出了两点:

  1. 结果按月排序。
  2. 购买总额可以是blanknull

基于这些,我将提出这种方法:

您可以获得给定月份的总数,您只需要处理total_pushase 为空的情况(作为旁注,拥有Purchase 的实例没有任何意义@987654327 @ 为空,至少必须为 0)。

阅读 Django Conditional expressions 以了解有关 WhenCase 的更多信息。

# Annotate the filtered objects with the correct value (null) is equivalent
# to 0 for this requirement.

result = Purchase.objects.filter(date__gte=start_date, date__lt=end_date).annotate(
    real_total = Case(
        When(total_purchase__isnull=True, then=0),
        default=F('total_purchase')
    )
)

# Then if you want to know the total for a specific month, use Sum.
month_partial_total = result.filter(
    date__month=selected_month
).aggregate(
    partial_total=Sum('real_total')
)['partial_total']

你可以在函数中使用它来达到你想要的结果:

import calendar
import collections
import dateutil

def totals(start_date, end_date):
    """
    start_date and end_date are datetime.date objects.
    """

    results = collections.OrderedDict()  # Remember order things are added.

    result = Purchase.objects.filter(date__gte=start_date, date__lt=end_date).annotate(
        real_total = Case(
            When(total_purchase__isnull=True, then=0),
            default=F('total_purchase')
        )
    )

    date_cursor = start_date
    month_partial_total = 0
    while date_cursor < end_date:
        # The while statement implicitly orders results (it goes from start to end).
        month_partial_total += result.filter(date__month=date_cursor.month).aggregate(
            partial_total=Sum('real_total')
        )['partial_total']


        results[date_cursor.month] = month_partial_total

        # Uncomment following line if you want result contains the month names
        # instead the month's integer values.
        # result[calendar.month_name[month_number]] = month_partial_total

        date_cursor += dateutil.relativedelta.relativedelta(months=1)

    return results

由于 Django 1.11 可能能够解决这个问题SubQueries,但我从未将它用于同一模型上的子查询。

【讨论】:

  • 我使用的是 django 版本 2.0.6,这里它给了我这个错误 "AttributeError: 'str' object has no attribute 'month'"
  • @NiladryKar 我写的函数期望 datetime.date 对象作为参数。你提供字符串吗?
  • 哦,是的,那是我的错误对不起...但是在纠正我的错误之后,现在我在这行代码中遇到了这个错误“TypeError:'QuerySet'对象不支持项目分配”结果[month_number] = month_partial_total"
  • @NiladryKar 那是我的错,我将相同的名称放入保存结果和第一个查询的字典中,我已修复它。
  • @NiladryKar 我也解决了这个问题。我希望这会有所帮助。另一方面,我也希望您理解这是为了朝着正确的方向解决您的问题而做出的努力,答案并不需要解决您的确切任务才能正确。
猜你喜欢
  • 2021-01-20
  • 2014-07-30
  • 1970-01-01
  • 1970-01-01
  • 2016-11-18
  • 2020-01-03
  • 2011-07-07
  • 2017-09-23
  • 2023-03-29
相关资源
最近更新 更多