在Django的paginate_queryset中设置自定义页码:获取用户所在页码并返回对应页面的实现问题
Looks like you’ve already nailed the core idea of calculating the user’s page, but there are two key fixes needed here: a bug in your page number calculation, and actually using that calculated page to return the right paginated data instead of the default page.
1. Fix the user_page Calculation
Your current logic counts users with lower XP than the current user, which gives an incorrect page number when using a descending XP sort (order_by('-xp_balance')).
Instead, you need to count users with higher XP than the current user—these are the users that appear before them in the scoreboard. The correct formula for the user’s page is:
# Count users with MORE XP than the current user users_above = User.objects.filter(xp_balance__gt=request.user.xp_balance).count() # Calculate page number: (number of users above // page size) + 1 user_page = (users_above // page_size) + 1
Let’s test this with an example:
- Current user XP: 100
- 3 users have higher XP, 2 have lower XP
- Page size: 3
users_above = 3, so(3 // 3) +1 = 2—which is correct, since the user falls in the 2nd page (positions 4-6 in the sorted scoreboard).
2. Modify the View to Return the Correct Page
DRF’s default pagination pulls the page number from the page query parameter in the request. To return your calculated user_page instead, you can temporarily override the request’s query parameters to use this page number, then leverage DRF’s built-in pagination logic.
Here’s the updated get_user_page method that fixes both issues:
@action(url_path='user-page', methods=['get'], detail=False, url_name='user-page') def get_user_page(self, request): # Get page size (use request param or default from pagination class) page_size = int(request.query_params.get('page_size')) if request.query_params.get('page_size') else self.pagination_class.page_size # Fix: Calculate users with higher XP to get the correct page number users_above = User.objects.filter(xp_balance__gt=request.user.xp_balance).count() user_page = (users_above // page_size) + 1 # Create a copy of query params to modify (avoid mutating the original request) modified_query_params = request.query_params.copy() # Set the page parameter to our calculated user_page modified_query_params['page'] = str(user_page) modified_query_params['page_size'] = str(page_size) # Ensure consistent page size # Update the request's query params to use our values request.query_params = modified_query_params # Use DRF's built-in pagination to fetch the correct page queryset = self.filter_queryset(self.get_queryset()) page = self.paginate_queryset(queryset) if page is not None: serializer = self.get_serializer(page, many=True) # Return paginated response with metadata (count, next/previous links) return self.get_paginated_response(serializer.data) # Fallback if pagination isn't applied (unlikely here, but safe to include) serializer = self.get_serializer(queryset, many=True) return Response(serializer.data)
Key Changes Explained:
- Fixed Page Logic: Now counts users with higher XP to accurately position the user in the sorted scoreboard.
- Modified Query Params: We create a copy of the request’s query parameters to set the
pagevalue to our calculateduser_page, ensuring DRF uses this page for pagination. - Consistent Response Format: Using
self.get_paginated_response()returns standard pagination metadata (like total user count, next/previous page links) alongside the page data, matching the format of your other list endpoints.
Edge Cases to Keep in Mind
- Tied XP Values: If other users have the same XP as the current user, you might want to adjust the
users_abovecount to usexp_balance__gteinstead ofgt—this groups all users with equal XP on the same page, depending on your ranking rules. - Large Page Sizes: If the page size is bigger than the total number of users,
user_pagewill default to 1, which is correct.
内容的提问来源于stack exchange,提问作者alighorbani

