Попытка использовать общее количество полей связанных объектов в запросе Django ORM - PullRequest
0 голосов
/ 02 апреля 2019

Предположим, у меня есть две модели:

class Task(Model):
    duration = models.IntegerField(default=100)

class Record(Model):
    minutes_planned = models.IntegerField(default=0)
    task = models.ForeignKey(Task, related_name=records)

Я хотел бы получить доступ ко всем объектам, у которых общее количество минут, запланированное для всех связанных записей, меньше, чем продолжительность объекта. У меня были проблемы с поиском решения в документах. Может ли кто-нибудь указать мне на это?

Task.objects.filter(duration__gt=F('records__minutes_planned')))

Task.objects.filter(duration__gt=Sum('records__minutes_planned'))

Task.objects.filter(duration__gt=Sum(F('records__minutes_planned')))

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

Кажется, что Сумма ограничена использованием только в .aggregate(). Тем не менее, я хотел бы получить сами объекты, а не набор значений, что мне даст .aggregate().

UPDATE: Нашел эту часть документов , что выглядит многообещающе.

Ответы [ 2 ]

0 голосов
/ 02 апреля 2019

Вот окончательное решение, записанное в виде менеджера моделей:

from django.db.models import Manager, OuterRef, Subquery, Sum
from django.db.models.functions import Coalesce

class TaskManager(Manager):

    def eligible_for_planning(self, user):
        from .models import Record
        records = Record.objects.filter(task=OuterRef('pk')).order_by().values('task')
        minutes_planned = records.annotate(total=Sum('minutes_planned')).values('total')
        qs = self.model.objects.filter(user=user, status=ACTIONABLE, duration__gt=Coalesce(Subquery(minutes_planned), 0))
        return qs

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

В этом случае records - это второй запрос (или SubQuery), и он фильтрует записи по pk задачи, запрашиваемой в этом менеджере.

Затем minutes_planned возвращает фактическую сумму, которая будет сравниваться с duration Задачи.

Наконец, все это подключено к набору запросов как объект Subquery.Оберните его в Coalesce и добавьте значение по умолчанию, если не найдены объекты записи.В моем случае это ноль.

Ссылка

0 голосов
/ 02 апреля 2019

Попробуйте использовать annotate () .Вы можете аннотировать поле, которое содержит сумму minutes_planned всех Records, а затем использовать это значение для фильтрации необходимых Tasks.Запрос будет выглядеть примерно так:

Task.objects.annotate(total_minutes_planned=Sum('records__minutes_planned')).
   filter(duration__gt=total_minutes_planned)

Надеюсь, это поможет.

...