我对如何在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。
任何迹象或想法如何解决?
答案 0 :(得分:1)
我不确定我是否正确。在您的表格“类似的东西”中,您是否要month
引用模型中的不同字段(month_sold
或month_painted
),取决于您正在查看的汇总?因此,对于Total
和Profit
,它是month_sold
,对于area_1
和area_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_sold
或month_painted
)取决于您要成为外部联接外部部分的月份类型,即。即使其他月份类型没有对应的值,也要包含哪个月份类型。要使用ORM包括两个(= {FULL OUTER JOIN
),您首先必须获得所有月份的列表(无论是涂漆的还是出售的),然后将其他列作为单独的子查询。