【问题标题】:Getting the total of refundable items for each order line获取每个订单行的可退款项目总数
【发布时间】:2020-07-11 06:42:48
【问题描述】:

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

因此,对于 Order 的每个 OrderLine,我想计算引用 Orderline 的所有现有 RefundedLine 的 number 属性的总和。

然后将此总和与 OrderLine 编号 属性进行比较:如果相等,则无法再退款,如果小于则可以退款 (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: can't adapt type 'QuerySet'

当我尝试遍历refunded_lines


我还有其他尝试,但我认为写它们不会有好处,因为帖子已经很混乱了。

【问题讨论】:

    标签: django postgresql django-queryset


    【解决方案1】:

    尝试以下方法:

    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
    )
    

    【讨论】:

    • 感谢您的快速回答。离我预期的结果不远。 1个问题,当他们没有refundedline时,我没有退货,所以没有OrderLine。虽然如果 Sum('refundedline__number') 为 None 这意味着它可以退款。如何解决?而且我不明白我们如何引用'refundedline__number'。我的模型中没有指定相关的名称或字段,不应该是refundedline_set 吗?最后一件事,它不按顺序过滤,我想我可以在性能注释之前过滤,或者在你的过滤器中过滤?
    • 我得到了 None 问题的答案(使用 Coalesce)。我要回答我自己的问题。非常感谢,我知道一个简单的查询可以实现我所需要的。我花了 3 天时间与 Sum 函数作斗争,并用其他错误进行子查询...
    • 1.我更新了我的答案以排除没有 RefundedLine 指向它的 OrderLines。如果您只想将 None 值更改为零,也可以使用 Coalesce。或者您可以合并然后过滤掉零值 2. 在查询中访问相关名称时,您只需使用模型名称的小写即可。仅在从模型访问时使用 _set。 3.对于过滤,如果您的过滤器中不需要带注释的字段,您可以在注释之前进行。
    • 感谢您的详细评论!我尝试支持您的答案并将我的答案标记为已接受(因为它具有与 Sum() 返回 None 的情况有关的相应行为,关于我的初始问题),但它不起作用......(stackoverflow 信誉点问题? ) 无论如何,我得到的比我来的要多。谢谢!编辑:需要等2天才能接受,ok
    【解决方案2】:

    感谢@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
            )
    

    【讨论】:

    • 如果对我的回答有帮助,我将不胜感激:)
    • 正如您在回答的评论中所说,我尝试了多次,现在我认为它被赞成(至少当我刷新时投票仍然存在)在尝试发布此评论时也出现错误:(跨度>
    猜你喜欢
    • 2019-07-08
    • 1970-01-01
    • 1970-01-01
    • 2019-02-28
    • 2020-01-11
    • 2018-12-21
    • 2021-10-22
    • 1970-01-01
    • 2012-05-22
    相关资源
    最近更新 更多