如何在Django2中对queryset的两列进行分组?

时间:2019-02-04 17:02:17

标签: django django-views

我对如何在quesryset上使用.annotate感到困惑。

为了快速:我有一个模型:

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')

但显然,它按month_sold分组。因此,总的来说,利润值不错,但我不知道如何按month_painted来获得area_1和_2。

任何迹象或想法如何解决?

1 个答案:

答案 0 :(得分:1)

我不确定我是否正确。在您的表格“类似的东西”中,您是否要month引用模型中的不同字段(month_soldmonth_painted),取决于您正在查看的汇总?因此,对于TotalProfit,它是month_sold,对于area_1area_2,它是month_painted

如果是这样,您将不会通过一个查询来实现。在原始SQL中,您可以将表自身与month_sold = month_painted联接;在Djano的ORM中,我相信您需要针对未按主查询的月份类型分组的每个集合的子查询。例如:

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_soldmonth_painted)取决于您要成为外部联接外部部分的月份类型,即。即使其他月份类型没有对应的值,也要包含哪个月份类型。要使用ORM包括两个(= {FULL OUTER JOIN),您首先必须获得所有月份的列表(无论是涂漆的还是出售的),然后将其他列作为单独的子查询。