[英]Multiple annotate with sum and display data in admin - Django
I'm new to both Django and Python. 我是Django和Python的新手。 Currently I'm trying the Django Admin by doing. 目前我正在尝试使用Django Admin。
I've three models for a Django app, which are GoodsItem
, SoldGoodsItem
and FinishedGoodsItem
. 我有三个Django应用程序模型,分别是GoodsItem
, SoldGoodsItem
和FinishedGoodsItem
。 The models.py is: models.py是:
from django.db import models
class GoodsItem(models.Model):
name = models.CharField(max_length=255)
size = models.DecimalField(max_digits=4, decimal_places=2)
INCHES = 'IN'
NUMBER = 'NUM'
GOODS_ITEM_SIZE_UNITS = (
(INCHES, 'Inches'),
(NUMBER, '#'),
)
size_unit = models.CharField(
max_length=4,
choices=GOODS_ITEM_SIZE_UNITS,
default=INCHES,
)
def __str__(self):
if(self.size_unit == self.NUMBER):
return "%s #%s" % (self.name, (self.size).normalize())
else:
return "%s %s\"" % (self.name, (self.size).normalize())
class FinishedGoodsItem(models.Model):
date = models.DateField()
goods_item = models.ForeignKey(GoodsItem, on_delete=models.CASCADE, related_name="finished_name")
weight = models.DecimalField(max_digits=6, decimal_places=3)
def __str__(self):
return str(self.goods_item)
class SoldGoodsItem(models.Model):
goods_item = models.ForeignKey(GoodsItem, on_delete=models.CASCADE, related_name="sold_name")
date = models.DateField()
weight = models.DecimalField(max_digits=6, decimal_places=3)
def __str__(self):
return str(self.goods_item)
And here is admin.py: 这是admin.py:
from django.contrib import admin
from django.db.models import Sum
from .models import GoodsItem, FinishedGoodsItem, SoldGoodsItem
@admin.register(SoldGoodsItem)
@admin.register(FinishedGoodsItem)
class FinishedGoodsItemAdmin(admin.ModelAdmin):
fields = ('date', 'goods_item', 'weight')
list_display = ('date', 'goods_item', 'weight')
@admin.register(GoodsItem)
class GoodsItemAdmin(admin.ModelAdmin):
list_display = ('__str__', 'finished_good', 'sold_good', 'stock_available')
def get_queryset(self, request):
qs = super(GoodsItemAdmin, self).get_queryset(request)
qs = qs.annotate(
finished_good=Sum('finished_name__weight'),
sold_good=Sum('sold_name__weight'),
stock_available=Sum('finished_name__weight') - Sum('sold_name__weight'),
)
return qs
def finished_good(self, obj):
return obj.finished_good
def sold_good(self, obj):
return obj.sold_good
def stock_available(self, obj):
return obj.stock_available
In stock_available
for each GoodsItem
, I want to display the difference between all entries of FinishedGoodsItem
and all entries of SoldGoodsItem
. 在stock_available
,每个GoodsItem
,我想显示FinishedGoodsItem
所有条目和SoldGoodsItem
所有条目之间的SoldGoodsItem
。 For now, I'm getting incorrect value for all three annotated fields which are finished_good
, sold_good
and stock_available
. 现在,我得到的所有三个带注释的字段的值都是不正确的,这些字段是finished_good
, sold_good
和stock_available
。 I'm unable to find the reason for that. 我无法找到原因。 In Django Debug Toolbar suggest that duplicate queries are being executed. 在Django Debug Toolbar中建议执行重复的查询。
This is known issue and occurs when we try to combine multiple aggregation, as mentioned in docs . 这是已知问题,当我们尝试组合多个聚合时发生,如文档中所述 。
As a workaround for this particular problem, we can use Subquery expression. 作为此特定问题的解决方法,我们可以使用子查询表达式。 Here is my updated admin.py using Subquery expression in get_queryset
method of GoodsItemAdmin
. 这是我在get_queryset
方法中使用子查询表达式更新的GoodsItemAdmin
。
from django.contrib import admin
from django.db.models import Subquery, Sum, OuterRef
from .models import GoodsItem, FinishedGoodsItem, SoldGoodsItem
@admin.register(SoldGoodsItem)
class SoldGoodsItemAdmin(admin.ModelAdmin):
fields = ('date', 'goods_item', 'weight')
list_display = ('date', 'goods_item', 'weight')
@admin.register(FinishedGoodsItem)
class FinishedGoodsItemAdmin(admin.ModelAdmin):
fields = ('date', 'goods_item', 'weight')
list_display = ('date', 'goods_item', 'weight')
@admin.register(GoodsItem)
class GoodsItemAdmin(admin.ModelAdmin):
list_display = ('__str__', 'finished_good', 'sold_good', 'stock_available')
def get_queryset(self, request):
qs = super(GoodsItemAdmin, self).get_queryset(request)
qs = qs.annotate(
finished_good = Subquery(FinishedGoodsItem.objects.filter(goods_item=OuterRef('pk'))\
.values('goods_item_id').annotate(sum=Sum('weight')).values('sum')[:1]),
sold_good = Subquery(SoldGoodsItem.objects.filter(goods_item=OuterRef('pk'))\
.values('goods_item_id').annotate(sum=Sum('weight')).values('sum')[:1])
)
return qs
def finished_good(self, obj):
return obj.finished_good
def sold_good(self, obj):
return obj.sold_good
def stock_available(self, obj):
finished_good = 0 if self.finished_good(obj) is None else self.finished_good(obj)
sold_good = 0 if self.sold_good(obj) is None else self.sold_good(obj)
return '-' if (finished_good == 0 and sold_good == 0) else finished_good - sold_good
Hope someone finds this useful. 希望有人觉得这很有用。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.