默认 pagenumberpagination 返回结构不满足需求是因为其响应数据嵌套在 results 键下且含 next/previous 链接,而前端或 openapi 规范常要求扁平化字段如 data、total、page 等;必须继承并重写 get_paginated_response 方法返回 response({ 'data': data, 'total': self.page.paginator.count, 'page': self.page.number, 'page_size': self.page_size, 'total_pages': self.page.paginator.num_pages })。

为什么默认的 PageNumberPagination 返回结构不满足需求?
Django REST Framework 默认分页器(如 PageNumberPagination)返回的数据是包裹在 results 键下的列表,外层还有 count、next、previous 等字段。但很多前端团队或第三方 API 规范(比如某些 OpenAPI 合约)要求分页字段扁平化,例如直接返回 data、total、page、page_size,甚至要求没有 next/previous 字符串链接——只返回数字页码。
这不是配置能解决的,必须重写 get_paginated_response 方法。
如何继承 PageNumberPagination 并覆盖响应结构?
最稳妥的方式是继承 DRF 的分页类,复写 get_paginated_response,手动构造字典。注意:不要改动 paginate_queryset,它负责切片逻辑,保持原样即可。
-
self.page.number是当前页码(从 1 开始),self.page.paginator.num_pages是总页数 -
self.page.paginator.count是总记录数,比len(self.page)更准(后者只返回当前页数据量) - 避免在响应里拼接
request相关 URL——除非你明确需要,否则容易在异步任务或测试中出错
from rest_framework.pagination import PageNumberPagination
class CustomPagination(PageNumberPagination):
def get_paginated_response(self, data):
return Response({
'data': data,
'total': self.page.paginator.count,
'page': self.page.number,
'page_size': self.page_size,
'total_pages': self.page.paginator.num_pages,
})
怎样在视图中正确启用自定义分页器?
不能只靠 DEFAULT_PAGINATION_CLASS 全局设置——因为不同接口可能需要不同结构(比如用户列表要 total_pages,日志列表只要 has_next 布尔值)。推荐在具体 APIView 或 ViewSet 中显式指定。
- 用
pagination_class = CustomPagination类属性,不是实例属性 - 如果视图集用了
ListModelMixin,确保没被get_paginated_response的 mixin 实现意外覆盖(DRF 会自动调用) - 测试时注意:未登录或权限不足导致空查询集时,
self.page仍存在,但self.page.object_list为空——你的total仍应返回真实总数(即paginator.count)
遇到 AssertionError: Expected a `Response`, received a `dict` 怎么办?
这是最常见的错误,发生在你忘了在 get_paginated_response 里调用 Response(...),而是直接 return 了一个 dict。DRF 的分页流程严格依赖返回 Response 实例。
另一个隐形坑是:如果你在自定义分页器里调用了 super().get_paginated_response(...),它会按默认格式返回,然后你再试图修改那个 response.data —— 这不行,因为 Response 初始化后 data 是只读的(除非用 .data = ... 赋值,但破坏封装且易漏)。
务必从头构造新 Response,别复用父类返回值。
django-filter 的 FilterSet 会影响 paginator.count 的计算时机,而缓存若只缓存 data 部分,就会漏掉 total 字段。这些细节不报错,但会让前端拿到的总数和实际翻页行为对不上。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











