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 许可协议。转载请注明出处!
相关文章
Django-Filter介绍与刁钻需求的实现方法
Django-Filter是一个非常好用的第三方库,很好的利用了Django ORM的特性,可以使用很少的代码就扩展原有的接口,实现多种筛选功能~ 场景 Model定义的部分代码,可以看到需求模型包括了 关键词 keyword、区域 region、需求状态 require_status,这三个字段 class Require(models.Model): 需求 keyword =
2021-12-18 · Django · 约 1 分钟
Django-Import-Export插件控制数据导入流程
前言 之前写过两篇跟这个插件有关的文章,可以回顾一下: Django数据导入导出神器django-import-export使用 Django-Import-Export插件关于外键的处理 最近有个朋友留言问我一个关于django-import-export插件的问题 为了形象表达这个问题,我举个书籍管理的例子来描述一下 数据库表 | id | name | price | | ---- |
2022-09-02 · Django · 约 1 分钟
Django应用部署:uwsgi+Nginx方式
环境准备 nginx+uwsgi方式部署顾名思义,需要nginx和uwsgi两个软件包。 nginx不用说,是必备的,关于nginx的安装本文不再赘述,详情可以自行搜索或者参考我以前的文章: Debian8搭建LEMP环境 安装uwsgi pip install uwsgi 上传项目代码 & 测试 # 进入项目目录,具体目录请自行选择 cd /path/to/project # 建立虚拟环境 v
2022-04-02 · Django · 约 1 分钟
给Django-Admin添加验证码和多次登录尝试限制
Django自带的Admin很好用,但是放到生产环境总还差了点什么= = 看看admin的介绍: Django奉行Python的内置电池哲学。它自带了一系列在Web开发中用于解决常见问题或需求的额外的、可选工具。这些工具和插件,例如django.contrib.redirects都必须在settings中的INSTALLED_APPS处进行注册,有的还需要执行manage.py migrate命令
2021-12-18 · Django · 约 1 分钟
讨论
共 0 条加载评论中…