我们通常在使用Django为前端或者用户提供接口的时候,时常会遇到返回大量数据信息的情况,比如:有1000条用户信息,有1000条新闻文章,有1000条商品信息等等。但是我们不可能将所有数据从数据库获取后一股脑的全部返回,这样相当不好,会增大服务器压力,同时也会增加前端渲染压力,也不方便前端或者用户使用。
这种情况下,我们就需要用到分页处理。顾名思义,就是将大量数据进行分页,每页只有少量数据,在用户希望通过接口获取数据时,只需要在url中加入指定的参数,就可以获取指定页的数据,使用相当方便。
在Django中,我们通常使用django-rest-framework这个插件进行分页,它提供了三种分页器:PageNumber分页(普通分页)、LimitOffset分页(偏移分页)、Cursor分页(加密分页),我们可以根据实际情况选择合适的分页器,下面来依次介绍这三种分页器。
class TestApi1(APIView):
"""PageNumber分页"""
def get(self, request):
page = PageNumberPagination() # 创建普通分页器对象
page.page_size = 3 # 设置每页数据条数
articles = Articles.get_articles() # 获取所有文章数据
ret_page = page.paginate_queryset(articles, request, self) # 用实例化的分页器对所有数据进行分页,并且返回当前页的数据的列表
articles_serializer = ArticlesSerializers(ret_page, many=True) # 将当前页数据序列化
data = {
'count': articles.count(), # 获取数据总数
'previous': page.get_previous_link(), # 获取上一页链接,如果没有则是None
'next': page.get_next_link(), # 获取下一页链接,如果没有则是None
'results': articles_serializer.data, # 当前页的序列化数据
}
return Response(data, status=200)
在调用接口时,需要添加page参数,表示获取哪一页,如果没有使用该参数,默认获取第一页,如果传入不存在的页数,则会返回404。
我这里的本地的api是:http://127.0.0.1:8000/api/v1/blog/test1,没有加参数表示获取第一页
观察数据,的确是我想要的格式,这样就可以了。再加入page参数试试:
class TestApi2(APIView):
"""LimitOffset分页"""
def get(self, request):
page = LimitOffsetPagination() # 创建偏移分页器对象
page.page_size = 3 # 设置每页数据默认条数,在api中可以通过limit参数修改
articles = Articles.get_articles() # 获取所有文章数据
ret_page = page.paginate_queryset(articles, request, self) # 用实例化的分页器对所有数据进行分页,并且返回当前页的数据的列表
articles_serializer = ArticlesSerializers(ret_page, many=True) # 将当前页数据序列化
data = {
'count': articles.count(), # 获取数据总数
'previous': page.get_previous_link(), # 获取上一页链接,如果没有则是None
'next': page.get_next_link(), # 获取下一页链接,如果没有则是None
'results': articles_serializer.data, # 当前页的序列化数据
}
return Response(data, status=200)
调用接口时,需要传入两个参数:offset、limit,其中,offset表示偏移量,即若offset=0,则从第一条数据算起,若offset=6,则从第七条数据开始算起,limit表示往后偏移多少,如:offset=2&limit=5表示从第3条数据是算起之后的第5条。若不传参数默认获取第一页。
Cursor分页(加密分页)和以上两种分页不太一样,它不能通过自由的传参来获取指定的页面,只能通过返回给你的上一页下一页的url来实现一页一页的跳转。其中还需要注意,Cursor分页默认会用created这个字段来排序,如果你的数据表中没有该字段,则可以使用分页器的ordering属性来自定义排序字段。
class TestApi3(APIView):
"""Cursor分页"""
def get(self, request):
page = CursorPagination() # 创建加密分页器对象
page.ordering = '-id' # 设置排序字段,按照id降序排列
page.page_size = 3 # 设置每页数据条数
articles = Articles.get_articles() # 获取所有文章数据
ret_page = page.paginate_queryset(articles, request, self) # 用实例化的分页器对所有数据进行分页,并且返回当前页的数据的列表
articles_serializer = ArticlesSerializers(ret_page, many=True) # 将当前页数据序列化
data = {
'count': articles.count(), # 获取数据总数
'previous': page.get_previous_link(), # 获取上一页链接,如果没有则是None
'next': page.get_next_link(), # 获取下一页链接,如果没有则是None
'results': articles_serializer.data, # 当前页的序列化数据
}
return Response(data, status=200)
第一步只能获取第一页:
这时可以看到我们获取到了加密后的下一页的url,我们可以通过请求这个url来获取下一页的数据。
通过以上描述,我们可以看到,我们每一个视图我们都需要去实例化一个分页器,然后去修改分页器的一些属性值来达到我们希望看到的效果,另外为了让数据清洗明了,我们每一次都还要单独去定义一个data,来声明返回数据的格式,虽然也不是很繁琐,但是如果太多的话也不太好。
在django-rest-framework中,使用通用视图或视图集时是能够自动执行分页的。我们需要在配置文件中进行设置。
实现PageNumber分页(以通用视图ListAPIView为例)
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 3,
}
class TestApi4(ListAPIView):
"""PageNumber分页"""
queryset = Articles.get_articles()
serializer_class = ArticlesSerializers
看,数据格式已经为我们定义好了,相当方便。
实现LimitOffset分页(以通用视图ListAPIView为例)
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 3,
}
class TestApi5(ListAPIView):
"""LimitOffset分页"""
queryset = Articles.get_articles()
serializer_class = ArticlesSerializers
实现Cursor分页(以通用视图ListAPIView为例)
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
'PAGE_SIZE': 3,
}
class TestApi6(ListAPIView):
"""Cursor分页"""
queryset = Articles.get_articles()
serializer_class = ArticlesSerializers
pagination_class = CursorPagination
pagination_class.ordering = '-id'
以上就是django-rest-framework分页功能的基本实现,一般都是够用的,如果需要有一些个性化的配置等等,可以参考官方文档。