Какие есть варианты, чтобы добиться поведения фильтра по объединенным кверисетам в Django?
В общем, проблема, которая у меня есть: Мне нужен вариант или альтернативный подход для фильтрации по аннотированным полям на union queryset.
У меня есть следующие упрощенные модели:
class Course(Model):
groups = ManyToManyField(through=CourseAssignment)
class CourseAssignment(Model):
course = ForeignKey(Course)
group = ForeignKey(Group)
teacher = ForeignKey(Teacher)
class Lesson(Model):
course = ForeignKey(Course, related_name='lessons')
class AssignmentProgress(Model):
lesson = ForeignKey(related_name='progresses')
course_assignment = ForeignKey(CourseAssignment)
student = ForeignKey(Student)
group = ForeignKey(Group)
status = CharField(choices=(
('on_check', 'On check'),
('complete', 'Complete'),
('assigned', 'Assigned'),
))
deadline = DateTimeField()
checked_date = DateTimeField()
Мне нужно вывести статистику выполнения заданий, сгруппированную по урокам и группам, для которых назначены курсы. Вот мой исходный набор запросов, обратите внимание, что уроки повторяются в конечном результате, разница в аннотированных данных:
def annotated_lessons_queryset():
lessons = None
for course_assignment in CourseAssignment.objects.all():
qs = Lesson.objects.filter(
course=course_assignment.course
).annotate(
completed_progresses=Count(
'progresses',
filter=Q(group=course_assignment.group),
output_field=IntegerField()
),
on_check=Exists(
AssignmentProgress.objects.filter(
lesson=OuterRef('id'), group=course_assignment.group, status='on_check'
)
)
)
lessons = qs if lessons is None else lessons.union(qs)
return lessons
Здесь я могу использовать оператор | OR, потому что он возвращает только разные значения уроков. Пока это работает, пока я не попробую отфильтровать все уроки с аннотированным статусом on_check:
qs = annotated_lessons_queryset().filter(on_check=True)
Который завершается с ошибкой:
raise NotSupportedError(
django.db.utils.NotSupportedError: Calling QuerySet.filter() after union() is not supported.
Пожалуйста, предложите обходной путь или другой подход, чтобы сделать этот набор запросов фильтруемым.
Я еще не пробовал это сделать, но, как говорится в сообщении об ошибке, вы должны использовать union() последним. Это немного сложно, поскольку "Уроки могут повторяться" в этом наборе вопросов. Поэтому я бы посоветовал использовать понимание списка, чтобы получить то, что вам нужно.
qs = annotated_lessons_queryset()
filtered = [lesson for lesson in qs if lesson.on_check]