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

如何在Django REST Framework开发的API中正确实现分页功能

Django Rest Framework 动态分页正确实现方案

你当前的硬编码写法仅能覆盖极个别固定参数场景,无法适配动态传入的页码、单页条数需求,且存在GET请求参数取值位置错误的问题:GET请求的URL参数应从request.query_params获取,而非用于读取请求体数据的request.data。
以下提供两种可直接落地的实现方式,完全匹配你需要的「传入page、rows参数,返回对应范围数据+总条目数」的需求。


方式一:手动实现动态分页逻辑(完全自定义逻辑,适配现有返回结构)

核心逻辑是先做参数校验与类型转换,再根据分页规则计算数据库查询的切片范围,直接通过ORM的分页语法在数据库层面完成数据截取,避免全量查询浪费性能。
完整视图代码如下:

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import DeviceControlPolicy
from .serializers import DeviceControlPolicySerializer

class DeviceControlPolicyView(APIView):
    def get(self, request):
        # 1. 读取分页参数,设置默认值
        try:
            # 页码默认第1页,单页条数默认10条
            page = int(request.query_params.get('page', 1))
            rows = int(request.query_params.get('rows', 10))
        except ValueError:
            # 参数传非数字时返回错误提示
            return Response(
                {"error": "page和rows参数必须为正整数"},
                status=status.HTTP_400_BAD_REQUEST
            )

        # 2. 参数合法性校验
        if page < 1 or rows < 1:
            return Response(
                {"error": "page和rows参数必须大于0"},
                status=status.HTTP_400_BAD_REQUEST
            )
        # 限制单页最大查询条数,防止拖垮数据库
        max_rows_per_page = 100
        if rows > max_rows_per_page:
            rows = max_rows_per_page

        # 3. 计算分页切片位置(ORM切片为左闭右开规则,页码从1开始计数)
        start_index = (page - 1) * rows
        end_index = page * rows

        # 4. 数据库查询:先查总条数,再查当前页数据,自动生成LIMIT/OFFSET SQL
        total_entries = DeviceControlPolicy.objects.count()
        page_data = DeviceControlPolicy.objects.all()[start_index:end_index]

        # 5. 序列化返回
        serializer = DeviceControlPolicySerializer(page_data, many=True)
        return Response({
            "details": serializer.data,
            "entry": total_entries
        })

方式二:使用DRF内置分页组件(推荐,可复用、少写重复代码)

DRF自带成熟的分页组件,不需要自己手写切片、参数校验逻辑,只需要自定义分页类适配你需要的返回格式即可,后续其他接口可以直接复用这套分页规则。

第一步:自定义分页类

可以在项目的公用utils文件里写,或者直接在当前视图所在文件写:

from rest_framework.pagination import PageNumberPagination

class CustomPagePagination(PageNumberPagination):
    # 指定URL中页码参数的名称,对应你的page参数
    page_query_param = "page"
    # 指定URL中单页条数参数的名称,对应你的rows参数
    page_size_query_param = "rows"
    # 默认单页条数
    page_size = 10
    # 单页最大允许条数
    max_page_size = 100

    # 重写返回格式,匹配你需要的details+entry结构
    def get_paginated_response(self, data):
        return Response({
            "details": data,
            "entry": self.page.paginator.count
        })

第二步:视图中直接引用分页类

代码会比手动实现简洁很多,不需要自己处理分页逻辑:

from rest_framework.views import APIView
from rest_framework.response import Response
from .models import DeviceControlPolicy
from .serializers import DeviceControlPolicySerializer
# 导入上面写的自定义分页类
from .pagination import CustomPagePagination

class DeviceControlPolicyView(APIView):
    def get(self, request):
        queryset = DeviceControlPolicy.objects.all()
        # 初始化分页类,自动完成参数校验、切片查询
        paginator = CustomPagePagination()
        page_result = paginator.paginate_queryset(queryset, request, view=self)
        serializer = DeviceControlPolicySerializer(page_result, many=True)
        return paginator.get_paginated_response(serializer.data)

关键注意点

  • 禁止全量查询数据后再在Python内存里做切片,一定要直接对QuerySet做切片,Django ORM会自动把切片逻辑转成数据库层面的LIMIT、OFFSET语句,性能差距会随数据量增大变得非常明显。
  • 必须设置单页最大条数限制,避免恶意请求传入超大rows参数导致数据库查询压力过高。
  • 如果后续需要加筛选、排序逻辑,直接在DeviceControlPolicy.objects.all()后面链式调用筛选方法即可,分页逻辑不需要改动。

内容的提问来源于stack exchange,提问作者Mukul

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 05:48:28