获取每个订单行的可退款商品总数
Getting the total of refundable items for each order line
我正在尝试获取仍可退款的特定订单的剩余 OrderLines。
因此,对于订单的每个 OrderLine,我想计算引用该订单的所有现有 RefundedLine 的 number 属性的总和。
然后将此金额与 OrderLine 编号 属性进行比较:如果相等,则该行不能再退款,如果少则可以退款(sum(RefundedLine number) < OrderLine number
).
(可以有多个 RefundedLine 引用一个 OrderLine,因为一个 Order 可以有单独的 Refund,直到它被全额退款)。
这是我的模型:
class Order(models.Model):
pass
class OrderLine(models.Model):
order = models.ForeignKey(Order,
on_delete=models.CASCADE,
related_name="order_lines")
number = models.SmallIntegerField(default=1,
validators=[MinValueValidator(1)])
class Refund(models.Model):
""" Is attached to one order, can concern multiples lines """
order = models.ForeignKey(Order, on_delete=models.PROTECT, editable=False)
lines = models.ManyToManyField(OrderLine, through='RefundedLine')
class RefundedLine(models.Model):
""" Used for m2m """
refund = models.ForeignKey(Refund, on_delete=models.CASCADE)
line = models.ForeignKey(OrderLine, on_delete=models.PROTECT)
number = models.SmallIntegerField(default=1,
validators=[MinValueValidator(1)])
现在我的尝试和他们的错误:
尝试 1:
return OrderLine.objects.filter(order=self.order_id,
number__lt=Sum(Subquery(
RefundedLine.objects.filter(line=OuterRef('pk')).values('number')
)))
ERROR: the column « cart_orderline.id » must appear in GROUP BY clause or must be used in an aggregate function
LINE 1: SELECT "cart_orderline"."id", "cart_orderline"."order_id", "...
自己翻译的错误信息不准确
尝试 2:
From the Django doc itself,没有错误,但没有按预期工作。如果 OrderLine 没有引用它的 RefundedLine,我会得到一个空查询集,而它应该 return OrderLine。
refunded_lines = RefundedLine.objects.filter(line=OuterRef('pk')).order_by().values('line')
total_refunded_lines = refunded_lines.annotate(total=Sum('number')).values('total')
refundable_lines = OrderLine.objects.filter(number__gt=Subquery(total_refunded_lines))
尝试 3:
refunded_lines = RefundedLine.objects.filter(line=OuterRef('pk')).order_by().values('line')
total_refunded_lines = refunded_lines.annotate(total=Sum('number', output_field=models.IntegerField())).values('total')
refundable_lines = OrderLine.objects\
.filter(order=self.order_id)\
.annotate(refundable_number=Case(
When(
Exists(total_refunded_lines),
then=Value(total_refunded_lines[:1]),
),
default=Value(0),
output_field=models.IntegerField()
)).filter(refundable_number__gt=0)
错误信息:
django.db.utils.ProgrammingError: can't adapt type 'QuerySet'
当我尝试循环 refunded_lines
时。
我还有其他尝试,但我不认为写它们会有好处,因为 post 已经很杂乱了。
尝试以下操作:
from django.db.models import Sum, F
OrderLine.objects.filter(
# do your filtering here if you do not need the annotated value
).annotate(
refunded_number=Coalesce(Sum('refundedline__number'),0) # Annotate each OrderLine with the sum of the numbers of its refundedline
).filter(
number__gt=F('refunded_number') # Use F() objects to reference a field
)
感谢@GrandPhuba 我可以测试并完成他的回答
# Added the missing filter for the order
refundable_lines= OrderLine.objects.filter(order=self.order_id).annotate(
# Added Coalesce because if no refundedline exist for the orderline, None will be returned and so the QuerySet will be empty, we do not want that
refunded_number=Coalesce(Sum('refundedline__number'), 0)
# Annotate each OrderLine with the sum of the numbers of its refundedline
).filter(
number__gt=F('refunded_number'), # Use F() objects to reference a field
)
我正在尝试获取仍可退款的特定订单的剩余 OrderLines。
因此,对于订单的每个 OrderLine,我想计算引用该订单的所有现有 RefundedLine 的 number 属性的总和。
然后将此金额与 OrderLine 编号 属性进行比较:如果相等,则该行不能再退款,如果少则可以退款(sum(RefundedLine number) < OrderLine number
).
(可以有多个 RefundedLine 引用一个 OrderLine,因为一个 Order 可以有单独的 Refund,直到它被全额退款)。
这是我的模型:
class Order(models.Model):
pass
class OrderLine(models.Model):
order = models.ForeignKey(Order,
on_delete=models.CASCADE,
related_name="order_lines")
number = models.SmallIntegerField(default=1,
validators=[MinValueValidator(1)])
class Refund(models.Model):
""" Is attached to one order, can concern multiples lines """
order = models.ForeignKey(Order, on_delete=models.PROTECT, editable=False)
lines = models.ManyToManyField(OrderLine, through='RefundedLine')
class RefundedLine(models.Model):
""" Used for m2m """
refund = models.ForeignKey(Refund, on_delete=models.CASCADE)
line = models.ForeignKey(OrderLine, on_delete=models.PROTECT)
number = models.SmallIntegerField(default=1,
validators=[MinValueValidator(1)])
现在我的尝试和他们的错误:
尝试 1:
return OrderLine.objects.filter(order=self.order_id,
number__lt=Sum(Subquery(
RefundedLine.objects.filter(line=OuterRef('pk')).values('number')
)))
ERROR: the column « cart_orderline.id » must appear in GROUP BY clause or must be used in an aggregate function LINE 1: SELECT "cart_orderline"."id", "cart_orderline"."order_id", "...
自己翻译的错误信息不准确
尝试 2:
From the Django doc itself,没有错误,但没有按预期工作。如果 OrderLine 没有引用它的 RefundedLine,我会得到一个空查询集,而它应该 return OrderLine。
refunded_lines = RefundedLine.objects.filter(line=OuterRef('pk')).order_by().values('line')
total_refunded_lines = refunded_lines.annotate(total=Sum('number')).values('total')
refundable_lines = OrderLine.objects.filter(number__gt=Subquery(total_refunded_lines))
尝试 3:
refunded_lines = RefundedLine.objects.filter(line=OuterRef('pk')).order_by().values('line')
total_refunded_lines = refunded_lines.annotate(total=Sum('number', output_field=models.IntegerField())).values('total')
refundable_lines = OrderLine.objects\
.filter(order=self.order_id)\
.annotate(refundable_number=Case(
When(
Exists(total_refunded_lines),
then=Value(total_refunded_lines[:1]),
),
default=Value(0),
output_field=models.IntegerField()
)).filter(refundable_number__gt=0)
错误信息:
django.db.utils.ProgrammingError: can't adapt type 'QuerySet'
当我尝试循环 refunded_lines
时。
我还有其他尝试,但我不认为写它们会有好处,因为 post 已经很杂乱了。
尝试以下操作:
from django.db.models import Sum, F
OrderLine.objects.filter(
# do your filtering here if you do not need the annotated value
).annotate(
refunded_number=Coalesce(Sum('refundedline__number'),0) # Annotate each OrderLine with the sum of the numbers of its refundedline
).filter(
number__gt=F('refunded_number') # Use F() objects to reference a field
)
感谢@GrandPhuba 我可以测试并完成他的回答
# Added the missing filter for the order
refundable_lines= OrderLine.objects.filter(order=self.order_id).annotate(
# Added Coalesce because if no refundedline exist for the orderline, None will be returned and so the QuerySet will be empty, we do not want that
refunded_number=Coalesce(Sum('refundedline__number'), 0)
# Annotate each OrderLine with the sum of the numbers of its refundedline
).filter(
number__gt=F('refunded_number'), # Use F() objects to reference a field
)