Fork me on GitHub

Python系列之《Django-DRF-分页》

DRF 分页了解一下 ^_^.

一、PageNumberPagination

此分页样式在请求查询参数中接受单个数字页码。

请求:

1
GET http://127.0.0.1:8000/users/?page=3

返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
HTTP 200 OK
{
"count": 197,
"next": "http://127.0.0.1:8000/users/?page=4",
"previous": "http://127.0.0.1:8000/users/?page=2",
"results": [
{
"id": 21,
"username": "pwd-2",
"email": "pwd-2@nicksors.cc"
},
]
}

1. 配置方法

要全局启用PageNumberPagination分页样式,请使用以下配置,并根据需要设置PAGE_SIZE

1
2
3
4
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10 # 每页显示十条数据
}

PageNumberPagination类允许你重写属性来修改分页的样式,其中有部分常用属性如下:

  • page_size - 表示页面大小的数值。如果设置,则会覆盖PAGE_SIZE设置。默认为与PAGE_SIZE设置键相同的值。
  • 其他的参数,官网介绍比较详细。

2. 基本使用

2.1 配置setting.py

1
2
3
4
5
6
$ vim setting.py

REST_FRAMEWORK = {
#分页
'PAGE_SIZE':10 #每页显示多少条数据
}

2.2 App视图使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ vim users/views.py

from rest_framework.pagination import PageNumberPagination

class UserViewset(viewsets.ReadOnlyModelViewSet):
"""
retrieve:
返回指定用户信息
list:
返回用户列表
"""
queryset = User.objects.all()
serializer_class = UserSerializer
pagination_class = PageNumberPagination # viewsets.ReadOnlyModelViewSet 里提供的 pagination_class 方法,可以从源码里看出,这里直接指定PageNumberPagination使用即可。

2.3 访问显示

很简单,基本就完成了分页需求。

2.4 全局配置

如果我们有非常多个非常多的App,非常多的viewset,那是不是需写很多次呢?答案是否定的。
其实我们可以实现配置一次,全局生效.

  1. setting.py

    1
    2
    3
    4
    REST_FRAMEWORK = {
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 10,
    }
  2. views.py
    去除viewset 视图里的pagination_class = PageNumberPagination

访问结果与上面一样。

2.5 自定义分页类

page_size是分页的一个BUG,如下面请求:

1
GET http://127.0.0.1:8000/users/?page=5&page_size=5

这里指定page_size显示5条数据,但结果依旧是默认的10条,如下:

这是什么原因导致的呢?通过调试源码发现问题:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class PageNumberPagination(BasePagination):

···略···

page_size_query_param = None

···略···

def get_page_size(self, request):
if self.page_size_query_param:
try:
return _positive_int(
request.query_params[self.page_size_query_param],
strict=True,
cutoff=self.max_page_size
)
except (KeyError, ValueError):
pass

return self.page_size

···略···

原因:
上面get_page_size函数调用page_size_query_param有问题,我们在前设置page_size_query_param = ‘page_size’ 不生效。所以导致get_page_size在判断的时候直接返回了self.page_size 而这个值就是默认的10条。

解决:
通过分析我们找到了原因,解决办法也就清晰了,直接将get_page_size重写即可,来看我这边如何处理

1)编写自定义分页类

vim opt/pagination.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from rest_framework.pagination import PageNumberPagination


class Pagination(PageNumberPagination):

def get_page_size(self, request):
try:
page_size = int(request.query_params.get('page_size', 0))
if page_size < 0:
return self.page_size
return page_size
except:
pass
return self.page_size

2)配置全局调用
vim setting.py

1
2
3
4
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'ops.pagination.Pagination', # 这里调用我们写的自定义分页类
'PAGE_SIZE': 10,
}

3)查看效果

没问题,使用DRF分页就到这里。

二、后言

DRF除了提供PageNumberPagination分页以外,还提供了LimitOffsetPaginationCursorPagination,那这两个分页大家可以参考下官网的介绍,作者使用后两个分页不是太多,那这里就不做过多介绍了。

下一篇将介绍DRF的搜索及使用,尽请关注。

-------------本文结束感谢您的阅读-------------