Django - annotate() - 对另一列进行过滤的列求和()

12

我有以下两个模型。

class Product(models.Model):
    product_group=models.ForeignKey('productgroup.ProductGroup', null=False,blank=False)
    manufacturer=models.ForeignKey(Manufacturer, null=False,blank=False)
    opening_stock=models.PositiveIntegerField(default=0)

    class Meta:
        unique_together = ('product_group', 'manufacturer')

TRANSACTION_TYPE=(('I','Stock In'),('O','Stock Out'))
class Stock(models.Model):
    product=models.ForeignKey('product.Product', blank=False,null=False)
    date=models.DateField(blank=False, null=False,)
    quantity=models.PositiveIntegerField(blank=False, null=False)
    ttype=models.CharField(max_length=1,verbose_name="Transaction type",choices=TRANSACTION_TYPE, blank=False)

我需要列出所有产品,其中stock_in_sum=所有库存入库之和stock_out_sum=所有库存出库之和blance_stock=期初库存+stock_in_sum-stock_out_sum

这是我目前已经实现的。

class ProductList(ListView):
    model=Product

    def get_queryset(self):
        queryset = super(ProductList, self).get_queryset()
        queryset = queryset.prefetch_related('product_group','product_group__category','manufacturer')
        queryset = queryset.annotate(stock_in_sum = Sum('stock__quantity'))
        queryset = queryset.annotate(stock_out_sum = Sum('stock__quantity'))

我需要获取每个产品对象以及:

  1. stock_in_sum 作为 ttype='I'quantity 和的总和
  2. stock_out_sum 作为 ttype='O'quantity 和的总和
  3. blance_stock 作为 product.opening_stock + stock_in_sum - stock_out_sum

我应该如何实现?

谢谢。

1个回答

20
您可以使用条件聚合
queryset = queryset.annotate(
    stock_in_sum = Sum(Case(When(stock__ttype='I', then=F('stock__quantity')), output_field=DecimalField(), default=0)),
    stock_out_sum = Sum(Case(When(stock__ttype='O', then=F('stock__quantity')), output_field=DecimalField(), default=0)))
)

使用F()表达式进行计算,然后再汇总以计算余额。

queryset = queryset.annotate(balance_stock=F('opening_stock') + F('stock_in_sum') - F('stock_out_sum'))

您还可以将不同的操作链接在一起,而不是进行多次赋值:

queryset = queryset.prefetch_related(...).annotate(...).annotate(...)

1
谢谢!它要求我使用ExpressionWrapper()。得到前两个数据如下:queryset = queryset.annotate( stock_in_sum = Sum(Case(When(stock__ttype='I', then= ExpressionWrapper(F('stock__quantity'), output_field=DecimalField())))) ) queryset = queryset.annotate( stock_out_sum = Sum(Case(When(stock__ttype='O', then= ExpressionWrapper(F('stock__quantity'), output_field=DecimalField())))) ) - art
1
但是 queryset = queryset.annotate( balance_stock = ExpressionWrapper(F('opening_stock') + F('stock_in_sum') - F('stock_out_sum'), output_field=DecimalField()) ) 对于任何一行中的 stock_in_sumstock_out_sum 为 None 的情况下都会返回 None。 - art
看起来我需要在某个地方返回0而不是None,但不知道怎么做。 - art
我编辑了答案,添加了默认值,这样它就不会尝试添加None,而是添加0。 - Nadège
太棒了!这给了我想要的确切结果。不再有重复查询 :) - art
如何获取库存中ttype=I/O的相关行数,并进行注释?stock_in_count = stock_in_count = Count(Case(When(stock__ttype='I', then=1), output_field=DecimalField(), default=0)), ??? - art

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接