Django:执行复杂注释和聚合时出现问题

Nil*_*Kar 4 django django-models django-aggregation django-annotate

这是型号:

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)
Run Code Online (Sandbox Code Playgroud)

我想在特定的日期范围内执行一个月份的"total_purchase"计算,如果一个月内没有购买,则总购买量应该是上个月的购买价值.如果在两个月内购买,那么总购买量会增加那两个......

例:

假设用户给出的日期范围是从4月到11月.

如果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
Run Code Online (Sandbox Code Playgroud)

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

谢谢

根据雷德尔米兰达先生的回答.我做了以下事情

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
Run Code Online (Sandbox Code Playgroud)

但现在输出就像这样(来自上面的例子):

April      2800
May        0
June       0
July       0
August     5000
September  0
October    6000
November   0
Run Code Online (Sandbox Code Playgroud)

有没有人知道如何在几个月之间添加......我想做点什么

e = month_partial_total + month_partial_total.next
Run Code Online (Sandbox Code Playgroud)

我想添加每个month_partial_total的下一个迭代值.我想这会解决我的问题..

任何人都知道如何在django执行此操作?

谢谢

Ray*_*nda 7

我在你的问题中注意到两件事:

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

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

你可以获得给定月份的总数,你只需要处理total_pushasenull为空的情况(作为旁注,没有任何意义让Purchasewhere 的实例total_purchase为null,至少它必须为0).

阅读Django条件表达式以了解有关When和的更多信息Case.

# 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']
Run Code Online (Sandbox Code Playgroud)

您可以在函数中使用它来实现您想要的结果:

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
Run Code Online (Sandbox Code Playgroud)

由于Django 1.11可能能够解决这个问题SubQueries,但我从未在同一模型上使用它来进行子查询.