获取每个订单行的可退款项目总数

问题描述 投票:0回答:1

我正在尝试获取仍可退款的特定订单的剩余订单行。

因此,对于订单的每个OrderLine,我想计算所有与Orderline相关的RefundedLine的number属性之和。

然后将这个总和与OrderLine number属性进行比较:如果相等,则该行不再可以退款,如果该行较少,则可以退款(sum(RefundedLine number) < OrderLine number)。

((可能有多个RefundedLine引用OrderLine,因为订单可以有单独的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')
                                        )))

错误:«Cart_orderline.id»列必须出现在GROUP BY子句中或必须在聚合函数中使用第1行:选择“ cart_orderline”。“ id”,“ cart_orderline”。“ order_id”,“ ...

错误消息不正确,因为它是我自己翻译的

尝试2:

From the Django doc itself,没有错误,但无法正常工作。如果OrderLine没有RefundedLine引用它,我将得到一个空的查询集,同时它应返回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:无法适应类型'QuerySet'

当我尝试循环到refunded_lines时。


[我还有其他尝试,但我认为写这篇文章不会是有益的,因为帖子已经很混乱了。

django postgresql django-queryset
1个回答
0
投票

尝试以下操作:

from django.db.models import Sum, F
OrderLine.objects.exclude(
    refundedline=None   # Exclude OrderLines with no RefundedLine pointing back to it
).annotate(
    refunded_number=Sum('refundedline__number')  # 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
)

0
投票

感谢@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
        )
© www.soinside.com 2019 - 2024. All rights reserved.