Как правильно использовать агрегат в Django - PullRequest
0 голосов
/ 06 мая 2019

У меня есть следующая модель:

class InvoiceEntry(models.Model):
    invoice = models.ForeignKey(Invoice, on_delete=models.CASCADE, related_name='entries', verbose_name=_("Invoice"))
    line = models.CharField(max_length=500, verbose_name=_("Print Line"))
    text = models.TextField(null=True, blank=True, verbose_name=_("Print Text"))
    amount = models.PositiveIntegerField(verbose_name=_("Amount"))
    unit_price = models.FloatField(null=True, blank=True, verbose_name=_("Unit Price"))
    content_type = models.ForeignKey(ContentType, on_delete=models.PROTECT, null=True, blank=True, validators=[validate_invoice_target_models])
    object_id = models.PositiveIntegerField(null=True, blank=True)
    is_separator = models.BooleanField(default=False)

Я хочу получить сумму за один счет, поэтому я получаю набор запросов, используя: invoice_entries = InvoiceEntry.objects.filter(invoice_id=2227), и если я применяю values('amount', 'unit_price'), я получаю:

{'amount': 0, 'unit_price': None}
{'amount': 4, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 1, 'unit_price': 11.5}
{'amount': 4, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 1, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 1, 'unit_price': 26.0}
{'amount': 2, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 2, 'unit_price': 11.5}
{'amount': 9, 'unit_price': 23.0}

Это в сумме составляет 716,00 , но когда я пытаюсь агрегировать:

total = InvoiceEntry.objects.filter(invoice_id=2227).aggregate(amount=Sum('amount', field=('amount * unit_price')))

Я получаю:

{'amount': 52}

Я не знаю, что я делаю неправильно.

Ответы [ 2 ]

0 голосов
/ 06 мая 2019

Если вы хотите выполнить агрегирование на основе счета, вам нужно использовать аннотирование. Агрегат будет работать со всеми счетами, представленными в модели. Это что-то вроде ниже без group by на языке sql.

select sum(column1 * column2) from table;

Annotate - это что-то с группировкой по.

select invoice, sum(column1 * column2) from table group by invoice;

Ниже набор запросов решит вашу проблему.

from your_app import models
from django.db.models import F, Sum, FloatField

m = models.InvoiceEntry.objects.values('invoice').annotate(sum=Sum(F('amount') * F('unit_price'), output_field=FloatField()))

<QuerySet [{'invoice': 1, 'sum': 48.0}, {'invoice': 2, 'sum': 8.0}]>

Это предоставит всю сумму счета. если вам нужен конкретный счет, вы можете использовать фильтр.

m = models.InvoiceEntry.objects.filter(invoice=1).values('invoice').annotate(sum=Sum(F('amount')
* F('unit_price'), output_field=FloatField()))

<QuerySet [{'invoice': 1, 'sum': 48.0}]>
0 голосов
/ 06 мая 2019

F-объекты в Django позволяют вам делать дополнительные вычисления.Объекты F генерируют выражения SQL, которые описывают операции на уровне базы данных.

Попробуйте, и это должно правильно подсчитать сумму.Предполагается, что вы используете Django 1.8 +

from django.db.models import Sum, F
InvoiceEntry.objects.filter(invoice_id=2227).aggregate(total=Sum(F('amount') * F('unit_price'))

Подробнее о выражениях F в Django здесь: https://docs.djangoproject.com/en/2.2/ref/models/expressions/#f-expressions

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...