Как ранжировать студентов по общему количеству баллов в Django QuerySet при фильтрации результатов
Я работаю над Django-приложением, в котором мне нужно ранжировать студентов на основе их общих баллов, а затем фильтровать результаты. Я хочу ранжировать всех студентов на основе их общих баллов, но показывать только студентов, которые являются детьми определенного пользователя (руководителя).
class StudentQuerySet(SetFieldQuerySetMixin, models.QuerySet):
"""QuerySet for Student model"""
def with_points(self) -> Self:
"""Adds a field indicating total points of a student."""
return self.annotate(
total_points=Coalesce(
models.Sum("attendances__feedback__overall"), Value(0)
)
)
def with_rank(self) -> Self:
"""Adds a field indicating rank of a student."""
self = self.with_points()
return self.annotate(
rank=models.Window(
expression=models.functions.Rank(),
order_by=models.F("total_points").desc(),
)
)
class StudentLeaderboardMeListView(generics.ListAPIView):
serializer_class = StudentLeaderboardSerializer
permission_classes = [IsSupervisor]
filter_backends = (DjangoFilterBackend,)
filterset_class = StudentFilter
def get_queryset(self):
all_students = Student.objects.all().with_rank()
top_3_ids = all_students.order_by("rank")[:3].values_list(
"id", flat=True
)
queryset = all_students.exclude(id__in=top_3_ids).filter(
parent=self.request.user
)
queryset = self.filter_queryset(queryset)
return queryset
def list(self, request, *args, **kwargs):
queryset = self.get_queryset()
page = self.paginate_queryset(queryset)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
Код правильно ранжирует всех студентов на основе их общих баллов, но на ранжирование влияет последующая фильтрация (exclude(id__in=top_3_ids).filter(parent=self.request.user))
. Мне нужно убедиться, что рейтинг вычисляется на основе всех студентов до применения фильтра. По сути, ранжирование не должно меняться на основе отфильтрованных результатов.
Как сделать так, чтобы рейтинг вычислялся по всему набору данных студентов, независимо от последующей фильтрации? Мне нужно, чтобы рейтинг rank
вычислялся по всем студентам, но при этом можно было отфильтровать и отобразить студентов, которые являются детьми вошедшего в систему руководителя.
Я нашел решение. Просто нужно использовать django-cte
библиотеку.
from django_cte import CTEManager, CTEQuerySet
class StudentQuerySet(SetFieldQuerySetMixin, CTEQuerySet, models.QuerySet):
...
class StudentManager(CTEManager, models.Manager):
...
а также на вид get_queryset()
вот так
def get_queryset(self):
all_students = Student.objects.all().with_rank()
student_ranks = With(all_students)
result = (
student_ranks.queryset()
.with_cte(student_ranks)
.filter(parent=self.request.user, rank__gt=3)
)
queryset = self.filter_queryset(result)
return queryset