Вы можете использовать аннотации и агрегацию для достижения этой цели. Во-первых, немного измените модель сеанса, изменив эту строку:
project = models.ForeignKey(Project, on_delete=models.CASCADE)
к этому:
project = models.ForeignKey(Project, related_name='sessions', on_delete=models.CASCADE)
- теперь каждый экземпляр Project
будет иметь поле sessions
, которое будет содержать набор запросов всех Session
s, связанных с этим Project
.
Вместо того, чтобы принимать все пользовательские сеансы, как вы делаете сейчас, вы можете взять все пользовательские проекты и циклически просмотреть сеансы каждого проекта, например:
projects = Project.objects.filter(user=self)
for p in projects:
sessions = p.sessions.all()
Затем вы можете манипулировать набором запросов sessions
, аннотируя их полем выражения, например:
from django.db.models import ExpressionWrapper, F, fields
duration_ = ExpressionWrapper(F('end') - F('start'), output_field=fields.DurationField())
sessions = p.sessions.annotate(d=duration_)
В этот момент у каждого члена набора запросов sessions
будет поле с именем d
, содержащее продолжительность соответствующего Session
.
Для суммирования длительностей мы можем использовать функцию агрегации наборов запросов Django, например:
from django.db.models import Sum
total = sessions.aggregate(total_duration=Sum('d'))["total_duration"]
То, что мы делаем во 2-й строке, - это создание одного элемента из набора запросов ( «агрегирование» его), добавление всех значений в поле d
и присвоение результата поле с именем total_duration
. Результат этого выражения:
sessions.aggregate(total_duration=Sum('d'))
- это dict
только с одним ключом (total_duration
), из которого мы берем значение.
Далее вы можете создать список проектов и продолжительности, а затем отсортировать их по продолжительности, например, как это:
import operator
plist = []
for p in projects:
sessions = p.sessions.annotate(d=duration_)
total = sessions.aggregate(total_duration=Sum('d'))["total_duration"]
# total holds the sum of this project's sessions
plist.append({'p':p,'total':total})
plist.sort(key=operator.itemgetter('total'))
projects = [item['p'] for item in plist]
Подводя итог:
import operator
from django.db.models import F, Sum, ExpressionWrapper, fields
duration_ = ExpressionWrapper(F('end') - F('start'), output_field=fields.DurationField())
projects = Project.objects.filter(user=self)
plist = []
for p in projects:
sessions = p.sessions.annotate(d=duration_)
total = sessions.aggregate(total_duration=Sum('d'))["total_duration"]
# total holds the sum of this project's sessions
plist.append({'p':p,'total':total})
plist.sort(key=operator.itemgetter('total'))
projects = [item['p'] for item in plist]
Ссылка: этот ответ , Выражения запросов Джанго , Агрегация Джанго