Джанго: проблема в выполнении сложных аннотаций и агрегации - PullRequest
0 голосов
/ 11 января 2019

Это модель:

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" в пределах определенного диапазона дат таким образом, чтобы, если в месяце нет покупок, общая сумма покупки равнялась стоимости покупки предыдущего месяца И если через два месяца будет покупка, то к сумме этих двух добавится сумма ...

Пример:

Предположим, диапазон дат, указанный пользователем, - с апреля по ноябрь.

При покупке 2800 долларов в апреле и 5000 долларов в августе и 6000 долларов в октябре.

Тогда результат будет таким:

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

Есть идеи, как это сделать в запросах 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

Но теперь вывод идет так (из примера выше):

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?

Спасибо

Ответы [ 2 ]

0 голосов
/ 16 января 2019

Решение

Согласно ответу г-на Райдела Миранды, наконец-то я получил решение своей проблемы ...

Я сделал следующее в своих взглядах, и это сработало очень хорошо:

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

Спасибо всем.

0 голосов
/ 14 января 2019

Я заметил две вещи в вашем вопросе:

  1. Результаты упорядочены по месяцам.
  2. Сумма покупки может быть blank или null.

На основании этих вещей я предложу этот подход:

Вы можете получить сумму за данный месяц, вам просто нужно обработать случай, когда total_pushase равен нулю (в качестве примечания, нет смысла иметь экземпляр Purchase, где total_purchase равно нулю, по крайней мере, должно быть 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']

Вы можете использовать это в функции для достижения желаемого результата:

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 может решить эту проблему Подзапросы , но я никогда не использовал его для подзапроса в той же модели.

...