<table style="height: 30px; background-color: #afeeee; width: 1266px; ; width: 1266px;" border="0">
<tr><td><span style="font-size: 16px;">一、简介</td>
</tr></table>
分页对于大多数网站来说是必不可少的,那你使用restful架构时候,你可以从后台获取数据,在前端利用利用框架或自定义分页,这是一种解决方案。当然django rest framework提供了分页组件,让我们可以更灵活的进行分页。
django rest framework提供了三种分页组件:
- PageNumberPagination:普通分页,查看第n页,每个页面显示n条数据
- LimitOffsetPagination: 基于位置的分页,在第n个位置,向后查看n条数据,和数据库的sql语句中的limit offset类似,参数offet代表位置,limit代表取多少条数据。
- CursorPagination:游标分页,意思就是每次返回当前页、上一页、下一页,并且每次的上一页和下一页的url是不规则的
<table style="height: 30px; background-color: #afeeee; width: 1266px; ; width: 1266px;" border="0">
<tr><td><span style="font-size: 16px;">二、每个分页组件使用</td>
</tr></table>
这里我们使用之前的模型,如果没有在setting中注册django rest framework 请注册它,为了方便我们查看分页,配置项在INSTALLED_APPS:
1.PageNumberPagination类分页
settings.py
models.py
user_type_choice =<span style="color: #000000;"> (
(1,<span style="color: #800000;">"<span style="color: #800000;">普通用户<span style="color: #800000;">"<span style="color: #000000;">),(2,<span style="color: #800000;">"<span style="color: #800000;">会员<span style="color: #800000;">"<span style="color: #000000;">),)
user_type = models.IntegerField(choices=<span style="color: #000000;">user_type_choice)
username = models.CharField(max_length=32,unique=<span style="color: #000000;">True)
password = models.CharField(max_length=64<span style="color: #000000;">)
group = models.ForeignKey(to=<span style="color: #800000;">'<span style="color: #800000;">UserGroup<span style="color: #800000;">',null=True,blank=<span style="color: #000000;">True)
roles = models.ManyToManyField(to=<span style="color: #800000;">'<span style="color: #800000;">Role<span style="color: #800000;">'<span style="color: #000000;">)
<span style="color: #0000ff;">class<span style="color: #000000;"> UserToken(models.Model):
user = models.OneToOneField(to=<span style="color: #000000;">UserInfo)
token = models.CharField(max_length=64<span style="color: #000000;">)
<span style="color: #0000ff;">class<span style="color: #000000;"> UserGroup(models.Model):
<span style="color: #800000;">"""<span style="color: #800000;">用户组<span style="color: #800000;">"""<span style="color: #000000;">
name = models.CharField(max_length=32,unique=<span style="color: #000000;">True)
<span style="color: #0000ff;">class<span style="color: #000000;"> Role(models.Model):
<span style="color: #800000;">"""<span style="color: #800000;">角色<span style="color: #800000;">"""<span style="color: #000000;">
name = models.CharField(max_length=32,unique=True)
urls.py
</span><span style="color: #008000;">#</span><span style="color: #008000;"> url(r'^api/v1/auth',views.AuthView.as_view()),</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> url(r'^api/v1/order',views.OrderView.as_view()),</span>
url(r<span style="color: #800000;">'</span><span style="color: #800000;">^api/v1/roles</span><span style="color: #800000;">'</span><span style="color: #000000;">,views.RoleView.as_view()),<span style="color: #ff6600;">#分页示例1</span>
url(r</span><span style="color: #800000;">'</span><span style="color: #800000;">^api/v1/userinfo</span><span style="color: #800000;">'</span><span style="color: #000000;">,views.UserinfoView.as_view()),url(r</span><span style="color: #800000;">'</span><span style="color: #800000;">^api/v1/group/(?P<xxx>\d+)</span><span style="color: #800000;">'</span>,views.GroupView.as_view(),name=<span style="color: #800000;">'</span><span style="color: #800000;">gp</span><span style="color: #800000;">'</span><span style="color: #000000;">),</span><span style="color: #008000;">#</span><span style="color: #008000;"> url(r'^api/(?P<version>[v1|v2]+)/user',views.UserView.as_view(),name="user_view"),</span>
]
本次我们使用roles来作为示例,并且为了更好的显示,此次会用到django rest framework 的响应(Response),后续会介绍,下面是对角色视图的序列化,这个已经在前面的序列化篇章中说明如下:
访问:http://127.0.0.1:8000/api/v1/roles,显示出所有的角色,如下:
加入分页后的角色视图:
同时,我们还需要配置每页显示的数据条数,在settings.py中:
此时我们访问:http://127.0.0.1:8000/api/v1/roles?page=1,则显示第一页,访问http://127.0.0.1:8000/api/v1/roles?page=2则显示第二页,如下图:
但是一般情况我们需要自己定义分页类,来定制更多的功能,示例:
自定义分页,更多的定制功能:
<span style="color: #800000;">"""<span style="color: #800000;">自定义分页<span style="color: #800000;">"""<span style="color: #000000;">
page_size=2 <span style="color: #008000;">#<span style="color: #008000;">默认每页显示个数配置
page_query_param = <span style="color: #800000;">'<span style="color: #800000;">p<span style="color: #800000;">' <span style="color: #008000;">#<span style="color: #008000;"> 页面传参的key,默认是page
page_size_query_param=<span style="color: #800000;">'<span style="color: #800000;">size<span style="color: #800000;">' <span style="color: #008000;">#<span style="color: #008000;"> 指定每页显示个数参数
max_page_size=4 <span style="color: #008000;">#<span style="color: #008000;"> 每页最多显示个数配置,使用以上配置,可以支持每页可显示2~4条数据
<span style="color: #0000ff;">class RolesSerializer(serializers.Serializer): <span style="color: #008000;">#<span style="color: #008000;">定义序列化类
id=serializers.IntegerField() <span style="color: #008000;">#<span style="color: #008000;">定义需要提取的序列化字段,**<span style="color: #000000;">kwargs):
roles=models.Role.objects.all() <span style="color: #008000;">#<span style="color: #008000;"> 获取所有数据
<span style="color: #000000;">
pg_obj=Mypagination() <span style="color: #008000;">#<span style="color: #008000;"> 实例化分页类
pg_res=pg_obj.paginate_queryset(queryset=roles,many=True) <span style="color: #008000;">#<span style="color: #008000;"> 对分完页码的数据进行序列化
<span style="color: #0000ff;">return Response(res.data)
访问:http://127.0.0.1:8000/api/v1/roles?p=1&size=3,需要注意的是此时的分页参数已经重写,查看结果:
自带返回上一页下一页功能:
<span style="color: #800000;">"""<span style="color: #800000;">自定义分页<span style="color: #800000;">"""<span style="color: #000000;">
page_size=2 <span style="color: #008000;">#<span style="color: #008000;">默认每页显示个数配置
page_query_param = <span style="color: #800000;">'<span style="color: #800000;">p<span style="color: #800000;">' <span style="color: #008000;">#<span style="color: #008000;"> 页面传参的key,many=True) <span style="color: #008000;">#<span style="color: #008000;"> 对分完页码的数据进行序列化
<span style="color: #0000ff;">return pg_obj.get_paginated_response(res.data) <span style="color: #ff6600;"># 使用分页自带的respose返回,具有上一页下一页功能
2.LimitOffsetPagination类分页
同样我们以角色视图做示例,通过自定义实现分页,示例:
default_limit </span>= 2 <span style="color: #008000;">#</span><span style="color: #008000;">默认显示的个数</span>
offset_query_param = <span style="color: #800000;">"</span><span style="color: #800000;">offset</span><span style="color: #800000;">"</span> <span style="color: #008000;">#</span><span style="color: #008000;">指定url中位置key值,其位置从0开始</span>
limit_query_param = <span style="color: #800000;">"</span><span style="color: #800000;">limit</span><span style="color: #800000;">"</span> <span style="color: #008000;">#</span><span style="color: #008000;"> 指定url中的偏移个数(显示个数)的key值</span>
max_limit = 10 <span style="color: #008000;">#</span><span style="color: #008000;">最多显示(偏移)的个数</span>
<span style="color: #0000ff;">class
RolesSerializer(serializers.Serializer): <span style="color: #008000;">#<span style="color: #008000;">定义序列化类id=serializers.IntegerField() <span style="color: #008000;">#<span style="color: #008000;">定义需要提取的序列化字段,**<span style="color: #000000;">kwargs):
roles=models.Role.objects.all() <span style="color: #008000;">#<span style="color: #008000;"> 获取所有数据
<span style="color: #000000;">
pg_obj=MyLimitOffsetPagination() <span style="color: #008000;">#<span style="color: #008000;"> 实例化分页类
pg_res=pg_obj.paginate_queryset(queryset=roles,many=True) <span style="color: #008000;">#<span style="color: #008000;"> 对分完页码的数据进行序列化
<span style="color: #0000ff;">return Response(res.data)
访问:http://127.0.0.1:8000/api/v1/roles?offset=1&limit=4(从第2个位置开始,查看4条数据),结果如下:
3.CursorPagination类实现分页(很少用)
示例:
cursor_query_param = <span style="color: #800000;">"<span style="color: #800000;">cursor<span style="color: #800000;">" <span style="color: #008000;">#<span style="color: #008000;">url获取分页的key
page_size = 2 <span style="color: #008000;">#<span style="color: #008000;">每页显示2个数据
ordering = <span style="color: #800000;">'<span style="color: #800000;">id<span style="color: #800000;">' <span style="color: #008000;">#<span style="color: #008000;">排序规则
page_size_query_param = <span style="color: #800000;">'<span style="color: #800000;">size<span style="color: #800000;">' <span style="color: #008000;">#<span style="color: #008000;">每页显示多少条参数配置
max_page_size = 5 <span style="color: #008000;">#<span style="color: #008000;">每页最多显示多少条数据
<span style="color: #0000ff;">class RolesSerializer(serializers.Serializer): <span style="color: #008000;">#<span style="color: #008000;">定义序列化类
id=serializers.IntegerField() <span style="color: #008000;">#<span style="color: #008000;">定义需要提取的序列化字段,**<span style="color: #000000;">kwargs):
roles=models.Role.objects.all() <span style="color: #008000;">#<span style="color: #008000;"> 获取所有数据
<span style="color: #000000;">
pg_obj=MyCursorPagination() <span style="color: #008000;">#<span style="color: #008000;"> 实例化分页类
pg_res=pg_obj.paginate_queryset(queryset=roles,many=True) <span style="color: #008000;">#<span style="color: #008000;"> 对分完页码的数据进行序列化
<span style="color: #0000ff;">return pg_obj.get_paginated_response(res.data)
访问http://127.0.0.1:8000/api/v1/roles,结果如下,从结果中可以看到下一页的url并不规则:
http://api.example.org/accounts/?page=4
http://api.example.org/accounts/?page=4&page_size=100
</span><span style="color: #800000;">"""</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> The default page size.</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> Defaults to `None`,meaning pagination is disabled.</span>
page_size =<span style="color: #000000;"> api_settings.PAGE_SIZE <span style="color: #ff6600;">#每页显示个数配置,可以在setting中配置,也可以在类里,当前类>全局(settings)</span>
django_paginator_class </span>=<span style="color: #000000;"> DjangoPaginator <span style="color: #ff6600;"># 本质使用django自带的分页组件
</span></span><span style="color: #008000;">#</span><span style="color: #008000;"> Client can control the page using this query parameter.</span>
page_query_param = <span style="color: #800000;">'</span><span style="color: #800000;">page</span><span style="color: #800000;">' <span style="color: #ff6600;"># url中的页码key配置</span></span><span style="color: #000000;">
page_query_description </span>= _(<span style="color: #800000;">'</span><span style="color: #800000;">A page number within the paginated result set.</span><span style="color: #800000;">'</span><span style="color: #000000;">) <span style="color: #ff6600;"> # 描述
</span></span><span style="color: #008000;">#</span><span style="color: #008000;"> Client can control the page size using this query parameter.</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> Default is 'None'. Set to eg 'page_size' to enable usage.</span>
page_size_query_param =<span style="color: #000000;"> None <span style="color: #ff6600;"> # url中每页显示个数的key配置</span>
page_size_query_description </span>= _(<span style="color: #800000;">'</span><span style="color: #800000;">Number of results to return per page.</span><span style="color: #800000;">'</span><span style="color: #000000;">)
</span><span style="color: #008000;">#</span><span style="color: #008000;"> Set to an integer to limit the maximum page size the client may request.</span>
<span style="color: #008000;">#</span><span style="color: #008000;"> Only relevant if 'page_size_query_param' has also been set.</span>
max_page_size =<span style="color: #000000;"> None <span style="color: #ff6600;"> # 最多显示个数配置</span>
last_page_strings </span>= (<span style="color: #800000;">'</span><span style="color: #800000;">last</span><span style="color: #800000;">'</span><span style="color: #000000;">,)
template </span>= <span style="color: #800000;">'</span><span style="color: #800000;">rest_framework/pagination/numbers.html</span><span style="color: #800000;">' <span style="color: #ff6600;"># 渲染的模板</span></span><span style="color: #000000;">
invalid_page_message </span>= _(<span style="color: #800000;">'</span><span style="color: #800000;">Invalid page.</span><span style="color: #800000;">'</span><span style="color: #000000;">) <span style="color: #ff6600;"> # 页面不合法返回的信息,当然我们也可以自己定制
</span></span><span style="color: #0000ff;">def</span> paginate_queryset(self,queryset,view=<span style="color: #000000;">None): <span style="color: #ff6600;"># 获取分页数据
</span></span><span style="color: #800000;">"""</span><span style="color: #800000;">
Paginate a queryset if required,either returning a
page object,or `None` if pagination is not configured for this view.
</span><span style="color: #800000;">"""</span><span style="color: #000000;">
page_size </span>=<span style="color: #000000;"> self.get_page_size(request) <span style="color: #ff6600;"># 调用get_page_size 获取当前请求的每页显示数量
</span></span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> page_size:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> None
paginator </span>=<span style="color: #000000;"> self.django_paginator_class(queryset,page_size)
page_number </span>= request.query_params.get(self.page_query_param,1<span style="color: #000000;">)
</span><span style="color: #0000ff;">if</span> page_number <span style="color: #0000ff;">in</span><span style="color: #000000;"> self.last_page_strings:
page_number </span>=<span style="color: #000000;"> paginator.num_pages
</span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
self.page </span>=<span style="color: #000000;"> paginator.page(page_number)
</span><span style="color: #0000ff;">except</span><span style="color: #000000;"> InvalidPage as exc:
msg </span>=<span style="color: #000000;"> self.invalid_page_message.format(
page_number</span>=page_number,message=<span style="color: #000000;">six.text_type(exc)
)
</span><span style="color: #0000ff;">raise</span><span style="color: #000000;"> NotFound(msg)
</span><span style="color: #0000ff;">if</span> paginator.num_pages > 1 <span style="color: #0000ff;">and</span> self.template <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None:
</span><span style="color: #008000;">#</span><span style="color: #008000;"> The browsable API should display pagination controls.</span>
self.display_page_controls =<span style="color: #000000;"> True
self.request </span>=<span style="color: #000000;"> request
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> list(self.page)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_paginated_response(self,data):
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> Response(OrderedDict([
(</span><span style="color: #800000;">'</span><span style="color: #800000;">count</span><span style="color: #800000;">'</span><span style="color: #000000;">,self.page.paginator.count),(</span><span style="color: #800000;">'</span><span style="color: #800000;">next</span><span style="color: #800000;">'</span><span style="color: #000000;">,self.get_next_link()),(</span><span style="color: #800000;">'</span><span style="color: #800000;">previous</span><span style="color: #800000;">'</span><span style="color: #000000;">,self.get_previous_link()),(</span><span style="color: #800000;">'</span><span style="color: #800000;">results</span><span style="color: #800000;">'</span><span style="color: #000000;">,data)
]))
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_page_size(self,request):
</span><span style="color: #0000ff;">if</span><span style="color: #000000;"> self.page_size_query_param:
</span><span style="color: #0000ff;">try</span><span style="color: #000000;">:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> _positive_int(
request.query_params[self.page_size_query_param],strict</span>=<span style="color: #000000;">True,cutoff</span>=<span style="color: #000000;">self.max_page_size
)
</span><span style="color: #0000ff;">except</span><span style="color: #000000;"> (KeyError,ValueError):
</span><span style="color: #0000ff;">pass</span>
<span style="color: #0000ff;">return</span><span style="color: #000000;"> self.page_size
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_next_link(self):
</span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> self.page.has_next():
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> None
url </span>=<span style="color: #000000;"> self.request.build_absolute_uri()
page_number </span>=<span style="color: #000000;"> self.page.next_page_number()
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> replace_query_param(url,self.page_query_param,page_number)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_previous_link(self):
</span><span style="color: #0000ff;">if</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> self.page.has_previous():
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> None
url </span>=<span style="color: #000000;"> self.request.build_absolute_uri()
page_number </span>=<span style="color: #000000;"> self.page.previous_page_number()
</span><span style="color: #0000ff;">if</span> page_number == 1<span style="color: #000000;">:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> remove_query_param(url,self.page_query_param)
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> replace_query_param(url,page_number)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_html_context(self):
base_url </span>=<span style="color: #000000;"> self.request.build_absolute_uri()
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> page_number_to_url(page_number):
</span><span style="color: #0000ff;">if</span> page_number == 1<span style="color: #000000;">:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> remove_query_param(base_url,self.page_query_param)
</span><span style="color: #0000ff;">else</span><span style="color: #000000;">:
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> replace_query_param(base_url,page_number)
current </span>=<span style="color: #000000;"> self.page.number
final </span>=<span style="color: #000000;"> self.page.paginator.num_pages
page_numbers </span>=<span style="color: #000000;"> _get_displayed_page_numbers(current,final)
page_links </span>=<span style="color: #000000;"> _get_page_links(page_numbers,current,page_number_to_url)
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> {
</span><span style="color: #800000;">'</span><span style="color: #800000;">previous_url</span><span style="color: #800000;">'</span><span style="color: #000000;">: self.get_previous_link(),</span><span style="color: #800000;">'</span><span style="color: #800000;">next_url</span><span style="color: #800000;">'</span><span style="color: #000000;">: self.get_next_link(),</span><span style="color: #800000;">'</span><span style="color: #800000;">page_links</span><span style="color: #800000;">'</span><span style="color: #000000;">: page_links
}
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> to_html(self):
template </span>=<span style="color: #000000;"> loader.get_template(self.template)
context </span>=<span style="color: #000000;"> self.get_html_context()
</span><span style="color: #0000ff;">return</span><span style="color: #000000;"> template.render(context)
</span><span style="color: #0000ff;">def</span><span style="color: #000000;"> get_schema_fields(self,view):
</span><span style="color: #0000ff;">assert</span> coreapi <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span> None,<span style="color: #800000;">'</span><span style="color: #800000;">coreapi must be installed to use `get_schema_fields()`</span><span style="color: #800000;">'</span>
<span style="color: #0000ff;">assert</span> coreschema <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span> None,<span style="color: #800000;">'</span><span style="color: #800000;">coreschema must be installed to use `get_schema_fields()`</span><span style="color: #800000;">'</span><span style="color: #000000;">
fields </span>=<span style="color: #000000;"> [
coreapi.Field(
name</span>=<span style="color: #000000;">self.page_query_param,required</span>=<span style="color: #000000;">False,location</span>=<span style="color: #800000;">'</span><span style="color: #800000;">query</span><span style="color: #800000;">'</span><span style="color: #000000;">,schema</span>=<span style="color: #000000;">coreschema.Integer(
title</span>=<span style="color: #800000;">'</span><span style="color: #800000;">Page</span><span style="color: #800000;">'</span><span style="color: #000000;">,description</span>=<span style="color: #000000;">force_text(self.page_query_description)
)
)
]
</span><span style="color: #0000ff;">if</span> self.page_size_query_param <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">not</span><span style="color: #000000;"> None:
fields.append(
coreapi.Field(
name</span>=<span style="color: #000000;">self.page_size_query_param,schema</span>=<span style="color: #000000;">coreschema.Integer(
title</span>=<span style="color: #800000;">'</span><span style="color: #800000;">Page size</span><span style="color: #800000;">'</span><span style="color: #000000;">,description</span>=<span style="color: #000000;">force_text(self.page_size_query_description)
)
)
)
</span><span style="color: #0000ff;">return</span> fields</pre>
而第二种LimitOffsetPagination,使用场景是当数据量比较大时候,只关心其中某一部分数据,推荐使用。
CursorPagination类型分页相对于PageNumberPagination有点在于,它避免了人为在url中自己传入参数进行页面的刷新(因为url不规则),缺点也显而易见只能进行上下页的翻阅。