过滤 Django 时保持带注释的排名
Keeping annotated rank when filtering Django
我有一个过滤查询集,其中我根据特定顺序注释了每个 object 的排名。
当我执行其他过滤器时,如按排名更改顺序保持不变。执行新的 .filter()
时出现问题
我在页面上有一个搜索功能,可以过滤 object 标题后的结果。但是当我这样做时,带注释的排名值会根据新查询中的结果数量而变化,而不是保持其原始值。
举例说明:
这是我的原始查询:
choices_filter = Choice.objects.filter(tournament=pk).annotate \
(rank=RawSQL("RANK() OVER(ORDER BY winrate DESC, pickrate DESC, times_played)", []))
这 returns 一个像这样的查询集:
Rank Title Winrate
1...........Apple...........55%
2...........Pear............47%
3...........Banana..........44%
4...........Orange..........35%
5...........Watermelon......31%
如果我执行 .order_by('title') 我得到的预期结果是:
Rank Title Winrate
1...........Apple...........55%
3...........Banana..........44%
4...........Orange..........35%
2...........Pear............47%
5...........Watermelon......31%
但是如果我改为执行 .filter(title__icontains='an') 我会得到这个:
Rank Title Winrate
1...........Banana..........44%
2...........Orange..........35%
而不是期望的:
Rank Title Winrate
3...........Banana..........44%
4...........Orange..........35%
我对 Django 和 python(以及数据库)的经验还不够,无法自行浏览。我花了一段时间才弄清楚如何注释排名并让它与分页等一起工作
如果相关,这是我的完整视图代码(我使用的是 Django Rest Framework 和 APIviews):
class GameDetailFilterView(APIView, PaginationHandlerMixin):
permission_classes = (AllowAny, )
pagination_class = FullPagination
serializer_class = ChoiceSerializer
def get(self, request, pk, *args, **kwargs):
choices_filter = Choice.objects.filter(tournament=pk).annotate \
(rank=RawSQL("RANK() OVER(ORDER BY winrate DESC, pickrate DESC, times_played)", []))
filter_condition = request.session['filter_condition']
if filter_condition is not None:
choices = choices_filter.filter(title__icontains=filter_condition)
else:
choices = choices_filter.order_by('rank')
page = self.paginate_queryset(choices)
if page is not None:
serializer = self.get_paginated_response(self.serializer_class(page,
many=True).data)
else:
serializer = self.serializer_class(choices, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
您可以使用 django-cte 库
from django_cte import CTEManager, With
# models.py
class Choice(Model):
objects = CTEManager()
# ... other fields like tournament
# query
cte = With(
Choice.objects.filter(tournament=pk).annotate \
(rank=RawSQL("RANK() OVER(ORDER BY winrate DESC, pickrate DESC,times_played)", [])))
qs = cte.queryset().with_cte(cte).filter(title__icontains='an')
并关注此 。
我有一个过滤查询集,其中我根据特定顺序注释了每个 object 的排名。
当我执行其他过滤器时,如按排名更改顺序保持不变。执行新的 .filter()
时出现问题我在页面上有一个搜索功能,可以过滤 object 标题后的结果。但是当我这样做时,带注释的排名值会根据新查询中的结果数量而变化,而不是保持其原始值。
举例说明:
这是我的原始查询:
choices_filter = Choice.objects.filter(tournament=pk).annotate \
(rank=RawSQL("RANK() OVER(ORDER BY winrate DESC, pickrate DESC, times_played)", []))
这 returns 一个像这样的查询集:
Rank Title Winrate
1...........Apple...........55%
2...........Pear............47%
3...........Banana..........44%
4...........Orange..........35%
5...........Watermelon......31%
如果我执行 .order_by('title') 我得到的预期结果是:
Rank Title Winrate
1...........Apple...........55%
3...........Banana..........44%
4...........Orange..........35%
2...........Pear............47%
5...........Watermelon......31%
但是如果我改为执行 .filter(title__icontains='an') 我会得到这个:
Rank Title Winrate
1...........Banana..........44%
2...........Orange..........35%
而不是期望的:
Rank Title Winrate
3...........Banana..........44%
4...........Orange..........35%
我对 Django 和 python(以及数据库)的经验还不够,无法自行浏览。我花了一段时间才弄清楚如何注释排名并让它与分页等一起工作
如果相关,这是我的完整视图代码(我使用的是 Django Rest Framework 和 APIviews):
class GameDetailFilterView(APIView, PaginationHandlerMixin):
permission_classes = (AllowAny, )
pagination_class = FullPagination
serializer_class = ChoiceSerializer
def get(self, request, pk, *args, **kwargs):
choices_filter = Choice.objects.filter(tournament=pk).annotate \
(rank=RawSQL("RANK() OVER(ORDER BY winrate DESC, pickrate DESC, times_played)", []))
filter_condition = request.session['filter_condition']
if filter_condition is not None:
choices = choices_filter.filter(title__icontains=filter_condition)
else:
choices = choices_filter.order_by('rank')
page = self.paginate_queryset(choices)
if page is not None:
serializer = self.get_paginated_response(self.serializer_class(page,
many=True).data)
else:
serializer = self.serializer_class(choices, many=True)
return Response(serializer.data, status=status.HTTP_200_OK)
您可以使用 django-cte 库
from django_cte import CTEManager, With
# models.py
class Choice(Model):
objects = CTEManager()
# ... other fields like tournament
# query
cte = With(
Choice.objects.filter(tournament=pk).annotate \
(rank=RawSQL("RANK() OVER(ORDER BY winrate DESC, pickrate DESC,times_played)", [])))
qs = cte.queryset().with_cte(cte).filter(title__icontains='an')
并关注此