Аннотация Django с вложенным фильтром - PullRequest
20 голосов
/ 07 января 2011

Возможно ли отфильтровать аннотацию?

На мой взгляд, что-то вроде этого (что на самом деле не работает)

Student.objects.all().annotate(Count('attendance').filter(type="Excused"))

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

Обходной путь - это

for student in Student.objects.all():
    student.num_excused_absence = Attendance.objects.filter(student=student, type="Excused").count()

Это работает, но выполняет много запросов в реальном приложении.это может быть непрактично долго.Я думаю, что этот тип операторов возможен в SQL, но я бы предпочел остаться с ORM, если это возможно.Я даже попытался сделать два отдельных запроса (один для всех студентов, другой для получения итоговых значений) и объединил их с |.Комбинация изменила общее количество: (

Некоторые мысли после прочтения ответов и комментариев

Я решил проблему посещаемости, используя дополнительные sql здесь .

  • Сообщение в блоге Тимми было полезно. Мой ответ основан на нем.
  • Ответ hash1baby работает, но кажется таким же сложным, как sql. Он также требует выполнения sql и добавления результата в цикл forЭто плохо для меня, потому что я собираю множество этих фильтрующих запросов. Мое решение создает большой набор запросов с большим количеством дополнительных фильтров и выполняет их все сразу.
  • Если производительность не имеет значения -Я предлагаю обходной цикл for. Это гораздо проще понять.

Ответы [ 3 ]

15 голосов
/ 28 января 2016

Начиная с Django 1.8 вы можете сделать это прямо в ORM:

students = Student.objects.all().annotate(num_excused_absences=models.Sum(
    models.Case(
        models.When(absence__type='Excused', then=1),
    default=0,
    output_field=models.IntegerField()
)))

Ответ адаптирован из другого вопроса SO на ту же тему

Я не тестировал приведенный выше пример, но выполнил нечто подобное в своем собственном приложении.

5 голосов
/ 12 марта 2011

Вы правы - django не позволяет фильтровать подсчитываемые связанные объекты, не применяя фильтр к первичным объектам и, следовательно, исключая те первичные объекты, которые не имеют связанных объектов после фильтрации.

Но при небольшой утечке абстракции можно подсчитывать группы с помощью запроса значений.

Итак, я собираю пропуски в словаре и использую их в цикле. Как то так:

# a query for students
students = Students.objects.all()
# a query to count the student attendances, grouped by type.
attendance_counts = Attendence(student__in=students).values('student', 'type').annotate(abs=Count('pk'))
# regroup that into a dictionary {student -> { type -> count }}
from itertools import groupby
attendance_s_t = dict((s, (dict(t, c) for (s, t, c) in g)) for s, g in groupby(attendance_counts, lambda (s, t, c): s))
# then use them efficiently:
for student in students:
    student.absences = attendance_s_t.get(student.pk, {}).get('Excused', 0)
0 голосов
/ 07 января 2011

Может быть, это будет работать для вас:

excused = Student.objects.filter(attendance__type='Excused').annotate(abs=Count('attendance'))

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

Вот ссылка на Документы агрегации Django , где обсуждается порядок фильтрации.

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