Агрегация Django: суммирование умножения двух полей

У меня есть примерно такая модель:

class Task(models.Model):
    progress = models.PositiveIntegerField()
    estimated_days = models.PositiveIntegerField()

Теперь я хотел бы сделать расчет Sum(progress * estimated_days) на уровне базы данных. Используя Django Aggregation, я могу получить сумму для каждого поля, но не сумму умножения полей.


person Raunak Agarwal    schedule 28.08.2012    source источник


Ответы (4)


Обновление: для Django >= 1.8 следуйте ответу @kmmbvnr.

возможно использование Django ORM:

вот что вы должны сделать:

from django.db.models import Sum

total = ( Task.objects
            .filter(your-filter-here)
            .aggregate(
                total=Sum('progress', field="progress*estimated_days")
             )['total']
         )

Примечание. Если два поля имеют разные типы, скажем, integer и float, тип, который вы хотите вернуть, должен быть передан в качестве первого параметра Sum.

Это поздний ответ, но я думаю, он поможет кому-то, кто ищет то же самое.

person sha256    schedule 10.11.2013
comment
Работает хорошо, спасибо, но имейте в виду, что kwarg field не задокументирован и что я не нашел никаких тестов по этому поводу в наборе тестов Django. - person Sébastien Trottier; 06.01.2014
comment
что делает поле «прогресс»? Я пытаюсь понять этот фрагмент кода, так как это именно то, что мне нужно - person Maor; 07.10.2015
comment
@Maor Я думаю, вы имеете в виду первый параметр Sum? Ну, тогда, как я уже говорил if the two fields are of different types, say integer & float, the type you want to return should be passed as the first parameter of Sum - person sha256; 07.10.2015
comment
@ sha256, я так понимаю, вы имеете в виду, что первым параметром Sum должно быть имя поля, тип которого вы хотите, чтобы возвращался результат? В этом случае результат будет возвращен в типе данных поля progress? - person Binoj D; 11.04.2016
comment
@BinojDavid, точно! - person sha256; 12.04.2016

В Django 1.8 и более поздних версиях теперь вы можете передать выражение в свой агрегат:

 from django.db.models import F

 Task.objects.aggregate(total=Sum(F('progress') * F('estimated_days')))['total']

Константы также доступны, и все можно комбинировать:

 from django.db.models import Value

 Task.objects.aggregate(total=Sum('progress') / Value(10))['total']
person kmmbvnr    schedule 22.06.2015
comment
Я думаю, что этот ответ легче понять и поддерживать. Кроме того, если типы этих полей различны, добавьте параметр output_field в агрегатную функцию. - person Neo Ko; 23.11.2015
comment
Я пытаюсь это сделать на django 1.7, но получаю AttributeError AttributeError: 'ExpressionNode' object has no attribute 'split', для какой версии моя версия django 1.7.10 - person Grijesh Chauhan; 15.02.2016
comment
если progress это ForienKey какой-то другой модели, например пусть это будет Product и у него есть поле price, как его форматировать? Task.objects.aggregate(total=Sum(F('progress.price') * F('estimated_days')))['total'] не работает - person ANFAS PV; 27.08.2020
comment
Это решение работает для меня в моем сценарии... Спасибо. - person Ajay Kumar; 02.06.2021

Решение зависит от версии Django.

  • джанго ‹ 1.8

    from django.db.models import Sum
    MyModel.objects.filter(<filters>).aggregate(Sum('field1', field="field1*field2"))
    
  • джанго >= 1.8

    from django.db.models import Sum, F
    MyModel.objects.filter(<filters>).aggregate(Sum(F('field1')*F('field2')))
    
person Antstud    schedule 15.03.2016
comment
Когда агрегация выполняется с полями разных типов, вам нужно использовать ExpressionWrapper. Пожалуйста, взгляните на это решение: stackoverflow.com/questions/55458958/ - person serfer2; 17.10.2019

У вас есть несколько вариантов:

  1. Необработанный запрос
  2. Недокументированный подход Emulbreh
  3. Создайте третье поле progress_X_estimated_days и обновите его в методе сохранения с перезаписью. Затем выполните агрегацию через это новое поле.

Перезапись:

class Task(models.Model):
   progress = models.PositiveIntegerField()
   estimated_days = models.PositiveIntegerField()
   progress_X_estimated_days = models.PositiveIntegerField(editable=False)

   def save(self, *args, **kwargs):
      progress_X_estimated_days = self.progress * self.estimated_days
      super(Task, self).save(*args, **kwargs)
person dani herrera    schedule 28.08.2012
comment
Да, на самом деле я оставил необработанный SQL или дополнительный атрибут в качестве последнего варианта. В любом случае спасибо. - person Raunak Agarwal; 29.08.2012