You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 22:35:15