Получение суммы подлежащих возврату товаров для каждой строки заказа - PullRequest
0 голосов
/ 30 марта 2020

Я пытаюсь получить оставшиеся OrderLines указанного c Order, который еще можно вернуть.

Поэтому для каждой OrderLine ордера я хочу вычислить сумму числа атрибут всех существующих RefundedLine, ссылающихся на строку заказа.

Затем эту сумму сравнивают с атрибутом NumberLine : если оно равно, строка больше не может быть возвращена, если она меньше он может быть возвращен (sum(RefundedLine number) < OrderLine number).

(может быть несколько RefundedLine, ссылающихся на OrderLine, поскольку ордер может иметь отдельный возврат до полного возмещения).

Вот мои модели:

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 или должен использоваться в статистической функции LINE 1: SELECT "cart_orderline". "id", "cart_orderline". "order_id", "...

Ошибка сообщение не является точным, так как оно было переведено себя

Попытка 2:

Из Django do c сама , нет ошибки, но не работает, как ожидалось. Если у 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'

Когда я пытаюсь сделать l oop более refunded_lines.


У меня есть другие попытки, но я не думаю, что написание их было бы полезным, поскольку пост уже сгруппирован.

Ответы [ 2 ]

1 голос
/ 30 марта 2020

Попробуйте следующее:

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
)
0 голосов
/ 30 марта 2020

Благодаря @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
        )
...