Django не уважает фильтры Prefetch в аннотациях

class Subject(models.Model):
    ...
    students = models.ManyToMany('Student')
    type = models.CharField(max_length=100)

class Student(models.Model):
    class = models.IntergerField()
    dropped = models.BooleanField()
    ...

subjects_with_dropouts = (
    Subject.objects.filter(category=Subject.STEM).
        prefetch_related(
            Prefetch('students', queryset=Students.objects.filter(class=2020))
            .annotate(dropped_out=Case(
                        When(
                            students__dropped=True,
                            then=True,
                        ),
                        output_field=BooleanField(),
                        default=False,
                    ))
            .filter(dropped_out=True)
)

Я пытаюсь получить все предметы из категории STEM, у которых есть выбывшие из класса 2020, но почему-то я получаю предметы, у которых есть выбывшие и из других классов.

Я знаю, что могу достичь с

subjects_with_dropouts = Subject.objects.filter(
    category=Subject.STEM,
    students__dropped=True,
    students__class=2020,
)

Но почему первый подход не работает? Я использую PostgreSQL.

При использовании prefetch объединение выполняется в python. Хорошим способом представить это является то, что в первом запросе у вас есть две таблицы. Одна из предметов, где есть хотя бы один студент, который выбыл (обратите внимание, что вы делаете агрегат (Case), поэтому есть JOIN с GROUP BY на student.id), и одна из студентов в классе 2020 года (это отдельно от объединения в первой таблице). В предварительной выборке просто говорится, что нужно соединить эти два отдельных запроса, используя сквозную таблицу, которая содержит оба их идентификатора, представляющих соединение, которое автоматически генерируется ManyToManyField.

Хорошим способом увидеть, что происходит на самом деле, является использование print(QuerySet.query), где QuerySet - это экземпляр QuerySet (Subject.objects.all()). Или, если у вас есть средства, django debug toolbar - это фантастический инструмент, который показывает вам EXPLAIN оператор каждого запроса в каждой конечной точке.

Вернуться на верх