Implementing Three Pagination Features of Django REST Framework - Encapsulated in the DjangoStarter Project Template
Preface Continuing the Django backend development series. I happened to run into a pagination requirement, so I'll document it here. Django, as an "all-in-one" framework, has everything built in, including a pagination component. However, the default pagination component isn't optimized for API development, so Django REST Framework, which is specifically for writing APIs, wraps Django's pagination component another layer, making it more convenient when integrated into viewsets. However, we can't always use viewsets; some APIs still need the more flexible
目录7 个章节
Preface
Continuing the Django backend development series. I happened to run into a pagination requirement, so I'll document it here.
Django, as an "all-in-one" framework, has everything built in, including a pagination component. However, the default pagination component isn't optimized for API development, so Django REST Framework, which is specifically for writing APIs, wraps Django's pagination component another layer, making it more convenient when integrated into viewsets.
However, we can't always use viewsets; some APIs still need the more flexible ApiView, but ApiView can't directly use the default pagination component, so we need to wrap it ourselves.
Moreover, Django REST Framework's default pagination info isn't complete enough—for example, it doesn't include the total number of pages. We can also hack this a bit when wrapping it.
Pagination Methods in DRF
DRF provides three pagination methods for us:
PageNumberPagination: As the name suggests, no explanation neededLimitOffsetPagination: Offset paginationCursorPagination: Encrypted pagination
This article will only introduce the first, most commonly used one. The other two will also be wrapped, but due to space constraints I won't cover them. Interested readers can give them a try.
Let's Start Coding
First, in our DjangoStarter project, create a Python package named paginator under the utils directory.
Since there isn't much code, we can just write it in the utils/paginator/__init__.py file.
from collections import OrderedDict
from django.core.paginator import Paginator
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
from rest_framework.response import Response
class NumberPaginator(PageNumberPagination):
"""页码分页"""
def __init__(
self,
page_size,
page_size_query_param='page_size',
page_query_param='page',
max_page_size=None
):
"""
初始化分页
:param page_size: 每页显示多少条
:param page_size_query_param: URL中每页显示条数的参数
:param page_query_param: URL中页码的参数
:param max_page_size: 最大页码数限制
"""
self.page_size = page_size
self.page_size_query_param = page_size_query_param
self.page_query_param = page_query_param
self.max_page_size = max_page_size
def get_paginated_response(self, data):
paginator: Paginator = self.page.paginator
return Response(OrderedDict([
('total_item_count', paginator.count),
('page_count', paginator.num_pages),
('page_number', self.page.number),
('page_size', self.page_size),
('next', self.get_next_link()),
('previous', self.get_previous_link()),
('results', data)
]))
class LimitOffsetPaginator(LimitOffsetPagination):
"""Offset分页"""
default_limit = 1
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = 999
class CursorPaginator(CursorPagination):
"""加密分页"""
cursor_query_param = 'cursor'
page_size = 1
ordering = '-id' # 重写要排序的字段
To address the earlier issue that "Django REST Framework's default pagination info isn't complete enough", I overrode the get_paginated_response method and added these fields to the response:
page_count: total number of pagespage_number: current page numberpage_size: items per page
I also renamed the other two fields to make them more intuitive and more consistent with our usual development habits.
...
That completes the wrapping. Next, let's test it in code.
Test Endpoint
Let's write a test endpoint to see how it works
from utils.paginator import NumberPaginator
@swagger_auto_schema(
method='get', operation_summary='测试分页功能',
manual_parameters=[
openapi.Parameter('page', openapi.IN_QUERY, type=openapi.TYPE_NUMBER),
openapi.Parameter('page_size', openapi.IN_QUERY, type=openapi.TYPE_NUMBER),
])
@api_view()
def test_page(request):
# 测试数据
data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
paginator = NumberPaginator(request.query_params.get('page_size', 10))
return paginator.get_paginated_response({
'data': paginator.paginate_queryset(queryset=data, request=request)
})
Configure the route
urlpatterns = [
path('test_page', views.test_page),
]
Test Results
The finished endpoint accepts two parameters, page and page_size. I declared these two parameters in the @swagger_auto_schema decorator so we can test them conveniently in the Swagger docs.
We set page_size=5, and the JSON data we get looks like this:
{
"message": "请求成功",
"code": 200,
"data": {
"total_item_count": 10,
"page_count": 2,
"page_number": 1,
"page_size": "5",
"next": "http://127.0.0.1:8005/core/test_page?page=2&page_size=5",
"previous": null,
"results": {
"data": [
1,
2,
3,
4,
5
]
}
}
}
Looks good, very clear.
That's a wrap.
Using It in a ViewSet
Of course, this wrapped pagination component can also be used in a ViewSet
For example, configure it directly
from utils.paginator import NumberPaginator
class TestViewSet(viewsets.ReadOnlyModelViewSet):
# ...
pagination_class = NumberPaginator
You can also configure it globally
Edit the settings.py file and modify the REST Framework configuration
DjangoStarter splits
settings.py; the REST Framework configuration is in theconfig/rest_framework.pyfile
REST_FRAMEWORK = {
# 设置分页(使用DjangoStarter封装的分页器)
'DEFAULT_PAGINATION_CLASS': 'utils.paginator.NumberPaginator'
}
References
_EOF_
Implementing Three Pagination Features of Django REST Framework - Encapsulated in the DjangoStarter Project Template
- 本文作者:
- 程序设计实验室
- 版权声明:
- 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
相关文章
分享一些2026年有意思的现代化Django生态组件
2026年了,若还在忍受石器时代的模板和冗余CRUD,趁早转行。Cotton和Inertia总算给Django这老古董续了命,Ninja这类工具已是开发及格线。看一眼这些现代化组件吧,别再用那些自我感动的低效代码来折磨审美了。
2026-01-21 · Django · 约 1 分钟
Django数据导入导出神器django-import-export使用
前言 Django以快速开发闻名,但是如果处理数据的导出导入还需要自己写脚本,那就有违“Python之禅”了…… 而且导数据通常需要不同的格式,Excel、csv、json等,每种格式的数据就要写一个脚本太麻烦了,这时直接祭出django-import-export这个神器,官方一句话介绍:django-import-export is a Django application and libra
2021-12-18 · Django · 约 1 分钟
Django的信号机制入门
概念 django自带一套信号机制来帮助我们在框架的不同位置之间传递信息。也就是说,当某一事件发生时,信号系统可以允许一个或多个发送者(senders)将通知或信号(signals)发送给一组接受者(receivers)。 (感觉就很像Qt的信号与槽机制) 信号系统包含以下三要素: 发送者-信号的发出方 信号-信号本身 接收者-信号的接受者 Django内置了一整套信号,下面是一些比较常用的: 在
2022-08-26 · Django · 约 1 分钟
Django应用部署:Docker容器化实践
在此之前,我一直用uwsgi+virtualenv+nginx方式进行应用部署,操作起来比较麻烦,而且依赖于服务器上的Python版本,服务的管理方面单纯uwsgi + pid算不上特别麻烦但总没有docker compose方便,无奈原本的服务器是32位系统的用不上docker,现在又增加了一个新服务器,所以赶紧把docker安排上~ 不得不说真的太简单太方便了,全程傻瓜式操作,也基本没像之前u
2021-12-18 · Django · 约 1 分钟
讨论
共 0 条加载评论中…