Django POST请求中如何对返回结果的items数组实现分页?
问题描述
我在Django项目中需要获取用户的所有点赞/踩记录,这些记录关联交易,返回结果里的items字段是用户点赞/踩的交易列表。请求支持以下可选参数:
{ "user_id": (通常从会话获取,管理员可查询指定用户), "like_kind_id": (1为赞/2为踩), "include_code_and_name": (布尔值), "page_size": (分页用整数) }
预期返回格式:
{ "user_id":id, "likes":[ { "like_kind":{ "id":id, "code":like_code, "name":like_name, }, "items":[ { "transaction_id":transaction_id, "time_of":date_created, },... ] } ] }
likes数组仅包含赞和踩两个元素。目前我能从request.data拿到page_size参数,但无法对items数组实现分页。我知道GET请求可通过在views.py中设置pagination_class实现分页,想问如何在POST请求中实现类似操作,并将分页类的page_size设为请求中的page_size参数?
相关代码
views.py
class LikesUserPaginationAPIView(PageNumberPagination): page_size = 1 page_size_query_param = 'page_size' max_page_size = 1 class LikesUserListAPIView(APIView): """ All likes of the user """ permission_classes = [IsAuthenticated] authentication_classes = [authentication.SessionAuthentication, authentication.TokenAuthentication] pagination_class = LikesUserPaginationAPIView @classmethod def post(cls, request, *args, **kwargs): like_kind = request.data.get('like_kind') include_code = request.data.get('include_code') page_size = request.data.get('page_size') pagination.PageNumberPagination.page_size = page_size if include_code is None: include_code = False else: if include_code == "False": include_code = False elif include_code == "True": include_code = True else: return Response("Should be True или False for include_code", status=status.HTTP_400_BAD_REQUEST) if like_kind is None: like_kind = "all" else: try: likekind = LikeKind.objects.get(id=like_kind) except LikeKind.DoesNotExist: return Response("ID doesn't exists ", status=status.HTTP_404_NOT_FOUND) context = {"include_code": include_code, "like_kind": like_kind} user_id = request.user.id # get the user_id from session # TODO # optionally other users with higher privilege can get access to other users' likes # need to check for the privilege (admin, coordinator, ..) # user_id = request.data.get('user_id') if user_id is not None: try: user = User.objects.get(id=user_id) users = [user, user, user, user, user] serializer = LikeUserSerializer(users, many=True, context=context) return Response(serializer.data) except User.DoesNotExist: return Response("ID doesn't exists ", status=status.HTTP_404_NOT_FOUND) return Response("user_id is not given", status=status.HTTP_400_BAD_REQUEST)
serializer.py
class LikeUserSerializer(serializers.ModelSerializer): likes = serializers.SerializerMethodField() user_id = serializers.SerializerMethodField() def get_user_id(self, obj): return obj.id def get_likes(self, obj): include_code = self.context.get('include_code') like_kind_id = self.context.get('like_kind') likes = [] if like_kind_id == "all": like_kinds = [(like_kind.id, like_kind.name, like_kind.get_icon_url()) for like_kind in LikeKind.objects.all()] else: like_kinds = [(like_kind.id, like_kind.name, like_kind.get_icon_url()) for like_kind in [LikeKind.objects.get(id=like_kind_id)]] # {"user_id": 1} if include_code: for like_kind in like_kinds: items = [] transactions_liked = [(like.transaction_id, like.date_created) for like in Like.objects.filter_by_user_and_like_kind(obj.id, like_kind[0])] for transaction1 in transactions_liked: items.append( { "transaction_id": transaction1[0], "time_of": transaction1[1], } ) likes.append( { "like_kind": { 'id': like_kind[0], 'name': like_kind[1], 'icon': like_kind[2], }, "items": items } ) return likes else: for like_kind in like_kinds: items = [] transactions_liked = [(like.transaction_id, like.date_created) for like in Like.objects.filter_by_user_and_like_kind(obj.id, like_kind[0])] for transaction1 in transactions_liked: items.append( { "transaction_id": transaction1[0], "time_of": transaction1[1], } ) likes.append( { "like_kind": { 'id': like_kind[0], }, "items": items } ) return likes class Meta: model = Like fields = ['user_id', 'likes']
urls.py
path('get-likes-by-user/', likes_views.LikesUserListAPIView.as_view()),
解决方案
1. 调整View逻辑,手动处理POST分页
因为APIView不会自动触发分页,需要手动实例化分页类,同时根据请求参数动态设置page_size:
class LikesUserListAPIView(APIView): permission_classes = [IsAuthenticated] authentication_classes = [authentication.SessionAuthentication, authentication.TokenAuthentication] pagination_class = LikesUserPaginationAPIView def post(self, request, *args, **kwargs): # 参数解析与验证 like_kind = request.data.get('like_kind', 'all') include_code = request.data.get('include_code', False) page_size = request.data.get('page_size') # 处理布尔值参数 if isinstance(include_code, str): include_code = include_code.lower() == 'true' if include_code not in [True, False]: return Response("include_code必须为True或False", status=status.HTTP_400_BAD_REQUEST) # 验证like_kind合法性 if like_kind != 'all': try: LikeKind.objects.get(id=like_kind) except LikeKind.DoesNotExist: return Response("like_kind_id不存在", status=status.HTTP_404_NOT_FOUND) # 获取用户ID(管理员逻辑后续补充权限判断) user_id = request.user.id if not user_id: return Response("user_id未提供", status=status.HTTP_400_BAD_REQUEST) try: user = User.objects.get(id=user_id) except User.DoesNotExist: return Response("用户ID不存在", status=status.HTTP_404_NOT_FOUND) # 初始化分页器并设置page_size paginator = self.pagination_class() if page_size: try: paginator.page_size = int(page_size) except ValueError: return Response("page_size必须为整数", status=status.HTTP_400_BAD_REQUEST) # 传递分页器与请求到上下文 context = { "include_code": include_code, "like_kind": like_kind, "paginator": paginator, "request": request } serializer = LikeUserSerializer(user, context=context) return Response(serializer.data)
2. 修改Serializer,对items列表分页
在get_likes方法中,使用分页器处理查询集,不再直接返回所有结果:
class LikeUserSerializer(serializers.ModelSerializer): likes = serializers.SerializerMethodField() user_id = serializers.SerializerMethodField() def get_user_id(self, obj): return obj.id def get_likes(self, obj): include_code = self.context.get('include_code') like_kind_id = self.context.get('like_kind') paginator = self.context.get('paginator') request = self.context.get('request') likes = [] # 获取对应的like_kind集合 if like_kind_id == "all": like_kinds = LikeKind.objects.all() else: like_kinds = [LikeKind.objects.get(id=like_kind_id)] for like_kind in like_kinds: # 获取未分页的查询集 liked_queryset = Like.objects.filter_by_user_and_like_kind(obj.id, like_kind.id) # 执行分页 page = paginator.paginate_queryset(liked_queryset, request) if page is not None: transactions_liked = [(like.transaction_id, like.date_created) for like in page] else: transactions_liked = [(like.transaction_id, like.date_created) for like in liked_queryset] # 构建items列表 items = [{ "transaction_id": tid, "time_of": toc } for tid, toc in transactions_liked] # 构建like_kind数据 like_kind_data = {'id': like_kind.id} if include_code: like_kind_data.update({ 'name': like_kind.name, 'icon': like_kind.get_icon_url() }) likes.append({ "like_kind": like_kind_data, "items": items }) # 返回带分页元数据的结果(可选) if paginator: paginated_response = paginator.get_paginated_response(likes) return paginated_response.data return likes class Meta: model = User # 修正模型关联,序列化对象是User而非Like fields = ['user_id', 'likes']
3. 补充优化点
- 调整分页类的
max_page_size为合理值(比如100),避免单次返回过多数据 - 管理员查询其他用户的逻辑需要补充权限校验(比如判断用户是否为管理员)
- 如果需要返回分页链接、总条数等元数据,可直接返回
paginator.get_paginated_response(likes).data
内容的提问来源于stack exchange,提问作者Talgat Bektleuov
相关产品推荐
相关产品推荐

