Как сгруппировать по двум столбцам на queryset в Django2? - PullRequest
0 голосов
/ 04 февраля 2019

Меня немного смущает вопрос о том, как использовать .annotate на Quesryset.

Чтобы быть быстрым: у меня есть модель:

class Row(models.Model):
    order = models.ForeignKey('order.Header', blank=True, null=True)
    qty = models.IntegerField(blank=True, null=True, default=0)
    name = models.CharField(default='', blank=True, null=True)
    total = models.DecimalField(max_digits=10, decimal_places=2,default=0, blank=True, null=True)
    profit = models.DecimalField(max_digits=10,decimal_places=2,default=0, blank=True, null=True)
    profit_percent = models.DecimalField(max_digits=6,decimal_places=2,default=0, blank=True, null=True)
    month_sold = models.IntegerField(default=0)
    month_painted = models.IntegerField(default=0)
    area_painted_1 = models.DecimalField(max_digits=5,decimal_places=2,default=0, blank=True, null=True)
    area_painted_2 = models.DecimalField(max_digits=5,decimal_places=2,default=0, blank=True, null=True)

Что мне нужно сделать, это создатьсвоего рода сводка, которая будет показывать мне месяц за месяцем сумму итога, среднюю прибыль, а также сумму окрашенной области.

Примерно так:

+-------+-------+--------+----------+--------+--------+
| month | Total | Profit | Profit % | area_1 | area_2 |
+-------+-------+--------+----------+--------+--------+
| 0     | 23000 |   3000 | 13%      |     55 |     12 |
| Jan   | 10000 |   1000 | 10%      |     43 |     44 |
| April | 20000 |   1000 | 5%       |     99 |    134 |
+-------+-------+--------+----------+--------+--------+

Я пытался добиться этого с помощью .annotate:

result = Row.objects.values('month_sold') \
        .annotate(total=Sum('total')+1) \
        .annotate(profit=Sum('profit')) 
        .annotate(profit_percent=Round(F('profit')/F('total')*100, 2))                
        .annotate(area_2=Sum('area_painted_2'))
        .annotate(area_1=Sum('area_painted_1'))
        .values('month_sold', 'total', 'profit', 'profit_percent',
                'area_1', 'area_2')
        .order_by('moth_sold')

Но, очевидно, он группируется по месяцу.Итак, в целом, значения прибыли хороши, но я не знаю, как получить площадь_1 и _2 по месяцу / месяцу.

Какие-либо признаки или идеи, как я могу это решить?

1 Ответ

0 голосов
/ 05 февраля 2019

Я не уверен, что понял вас правильно.В вашей таблице «Нечто подобное», хотите ли вы, чтобы month ссылался на различные поля в вашей модели (или month_sold, или month_painted) в зависимости от того, на какой агрегат вы смотрите?Так что для Total и Profit это month_sold, а для area_1 и area_2 это month_painted?

Если это так, вы не достигнете этого с однимодиночный запрос.В сыром SQL вы можете объединить таблицу с самим собой на month_sold = month_painted;Я считаю, что в ORM Djano вам понадобятся подзапросы для каждого агрегата, который не сгруппирован по типу месяца основного запроса.Например:

sq1 = (
    Row.objects
    .filter(month_painted=OuterRef('month_sold'))
    .values('month_painted')
    .annotate(area_1=Sum('area_painted_1'))
    .values('area_1')
)
sq2 = (
    Row.objects
    .filter(month_painted=OuterRef('month_sold'))
    .values('month_painted')
    .annotate(area_2=Sum('area_painted_2'))
    .values('area_2')
)

result = (
    Row.objects
    .values('month_sold')
    .annotate(total=Sum('total')+1)
    .annotate(profit=Sum('profit')) 
    .annotate(profit_percent=Round(F('profit')/F('total')*100, 2))
    .annotate(area_1=Subquery(sq1, output_field=models.IntegerField()))
    .annotate(area_2=Subquery(sq2, output_field=models.IntegerField()))
    .values('month_sold', 'total', 'profit', 'profit_percent',
            'area_1', 'area_2')
    .order_by('month_sold')
)

В каких полях месяца (month_sold или month_painted) основной запрос и подзапросы являются базовыми в зависимости от того, какой тип месяца вы хотите использовать как внешнюю часть внешнего объединеният.е.какой тип месяца вы хотите включить, даже если для другого типа месяца нет соответствующих значений.Чтобы включить оба (= FULL OUTER JOIN) с использованием ORM, сначала необходимо получить список всех месяцев (независимо от того, были ли они нарисованы или проданы), а затем добавить другие столбцы как отдельные подзапросы.

...