7. 分页
如果有1000万条数据,怎么分页?记录下结尾的位置,下次检索,直接跳到那个位置,然后取多少个
a. 分页,看第n页,每页显示n条数据;(基于PageNumberPagination,最常用)
- 一:自己定义个类,继承自PageNumberPagination
- 二:直接用PageNumberPagination
from rest_framework.pagination import PageNumberPagination
class MyPageNumberPagination(PageNumberPagination):
page_size = 2 # 每页的数据条数
page_size_query_param = 'size' # 可以动态设置,这页展示多少条数据,但在max之下
max_page_size = 5 # 每页最大显示数量
page_query_param = 'page' # 查询某一页的查询字段
class Pager1View(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据(要给一个排序的顺序,进行分页,不然会报警告)
roles = models.Role.objects.all().order_by('-id')
# 创建分页对象
# pg = MyPageNumberPagination()
pg = PageNumberPagination()
# 在数据库中获取分页的数据
pager_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
# queryset = 从数据库拿到的东西
# request = request
# view = 当前处理数据的视图,基本上都是self
# 对数据进行序列化
ser = PagerSerialiser(instance=pager_roles, many=True)
#return Response(ser.data) # 直接返回对应数据
# 返回分页生成器携带上下页链接的数据
return pg.get_paginated_response(ser.data)
如下所示,多包括了上下页链接,总共的数量:
{
"count": 16,
"next": "http://127.0.0.1:8000/api/v1/pager1/?page=3",
"previous": "http://127.0.0.1:8000/api/v1/pager1/",
"results": [
{
"id": 3,
"user_type": 3,
"username": "lucy",
"password": "123"
},
{
"id": 4,
"user_type": 4,
"username": "admin",
"password": "admin"
}
]
}
b. 分页,在某个位置,向后查看n条数据;(基于LimitOffsetPagination)
from api.utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination
class MyLimitOffsetPagination(LimitOffsetPagination):
default_limit = 3 # 默认展示的数量
limit_query_param = 'limit' # 可查询的数据条数
offset_query_param = 'offset' # 基于第一条,向后偏移多少个(offset=0代表从第一个开始数)
max_limit = 7 # 在最大显示多少条
# 视图处理定义是一样
class Pager1View(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据(要给一个排序的顺序,进行分页,不然会报警告)
roles = models.Role.objects.all().order_by('-id')
# 创建分页对象
# pg = MyLimitOffsetPagination()
pg = LimitOffsetPagination()
# 在数据库中获取分页的数据
pager_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
# 对数据进行序列化
ser = PagerSerialiser(instance=pager_roles, many=True)
return Response(ser.data)
# return pg.get_paginated_response(ser.data)
c. 加密分页,上一页和下一页(基于CursorPagination,对页码链接进行加密,避免恶意操作)。
from api.utils.serializsers.pager import PagerSerialiser
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination
class MyCursorPagination(CursorPagination):
cursor_query_param = 'cursor' # 游标页码值,进行加密后的页码字符串
page_size = 2 # 每页显示的数据数量
ordering = 'id' # 排序方式('-id'表示按id倒序)
page_size_query_param = 'size' # 设置一个字段,通过url设置查询这页显示多少条数据
max_page_size = None # 每页最大显示数量
class Pager1View(APIView):
def get(self,request,*args,**kwargs):
# 获取所有数据(这个排序顺序在上边的ordering中指定了)
roles = models.Role.objects.all()
# 创建分页对象
# pg = CursorPagination()
pg = MyCursorPagination()
# 在数据库中获取分页的数据
pager_roles = pg.paginate_queryset(queryset=roles,request=request,view=self)
# 对数据进行序列化
ser = PagerSerialiser(instance=pager_roles, many=True)
# return Response(ser.data)
return pg.get_paginated_response(ser.data)
返回数据如下:
{
"next": "http://127.0.0.1:8000/api/v1/pager3/?cursor=cD00",
"previous": "http://127.0.0.1:8000/api/v1/pager3/?cursor=cj0xJnA9Mw%3D%3D",
"results": [
{
"id": 3,
"user_type": 3,
"username": "lucy",
"password": "123"
},
{
"id": 4,
"user_type": 4,
"username": "admin",
"password": "admin"
}
]
}
8. 视图
之前都是直接继承的
APIview
类,大部分东西都是自己写,当然还有其他的东西可以继承,
通过简单配置,实现分页、序列化等等功能。
a. 过去(Django的CBV模式)
class Pager1View(View):
pass
b. 现在 (rest framework的CBV模式)
class Pager1View(APIView): # View
pass
c. 无用(继承自APIView的类再次继承,基本不怎么用)(View --> APIView --> GenericAPIView)
from api.utils.serializsers.pager import PagerSerialiser
from rest_framework.generics import GenericAPIView
class View1View(GenericAPIView): # APIView
queryset = models.Role.objects.all()
serializer_class = PagerSerialiser
pagination_class = PageNumberPagination
def get(self,request,*args,**kwargs):
# 获取数据
roles = self.get_queryset() # models.Role.objects.all()
# [1, 1000,] [1,10]
pager_roles = self.paginate_queryset(roles)
# 序列化
ser = self.get_serializer(instance=pager_roles,many=True)
return Response(ser.data)
d. 类:GenericViewSet(ViewSetMixin, generics.GenericAPIView)
:添加了不少功能,可以以在类中进行配置的方式,实现某些功能。
- 路由:
url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list'})),
# 若继承自上边,则as_view方法中进行对应关系的设置,对于请求可以进行方法名的重定义
# key 代表请求过来的method名字, value为反射到的视图类中处理get请求的方法名。
- 视图:
from api.utils.serializsers.pager import PagerSerialiser
from rest_framework.viewsets import GenericViewSet
class View1View(GenericViewSet):
queryset = models.Role.objects.all() # 要查询的字段
serializer_class = PagerSerialiser # 序列化的类
pagination_class = PageNumberPagination # 执行分页的类
def list(self, request, *args, **kwargs):
# 获取数据
roles = self.get_queryset() # models.Role.objects.all()
# 进行分页[1, 1000,] [1,10]
pager_roles = self.paginate_queryset(roles)
# 序列化
ser = self.get_serializer(instance=pager_roles, many=True)
return Response(ser.data)
e. 最强大的类ModelViewSet
,继承了6个类,增、删、改、查、局部更新、GenericViewSet
(其中GenericViewSet上边还有父类,一个实现了as_view --> GenericAPIView --> APIView --> View)
class ModelViewSet(mixins.CreateModelMixin,
mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
mixins.ListModelMixin,
GenericViewSet):
"""
A viewset that provides default `create()`, `retrieve()`, `update()`,
`partial_update()`, `destroy()` and `list()` actions.
以上这几个方法实现增加、获取(查)、更新(改)、局部更新、删除、列表操作。
"""
pass
- 路由系统:
url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list','post':'create'})),
# 删改查都是基于对象ID来做的,所以这里要设置(?P<pk>\d+),使得视图函数能得到需要操作的对象的指针。
url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)/$', views.View1View.as_view({
'get': 'retrieve',
'delete':'destroy',
'put':'update',
'patch':'partial_update'})),
- 视图:
from api.utils.serializsers.pager import PagerSerialiser
from rest_framework.viewsets import GenericViewSet,ModelViewSet
from rest_framework.mixins import ListModelMixin,CreateModelMixin
# 在类中配置相应的字段
class View1View(ModelViewSet):
queryset = models.Role.objects.all() # 要查询的字段
serializer_class = PagerSerialiser # 序列化的类
pagination_class = PageNumberPagination # 执行分页的类
#**PS**: class View1View(CreateModelMixin,GenericViewSet):
#简单几行代码实现增删改查等基本功能
总结(使用继承类):
a. 增删改查
ModelViewSet
b. 增删CreateModelMixin
,DestroyModelMixin
,GenericViewSet
c. 复杂逻辑GenericViewSet
或APIView
9. 路由
a.最普通的路由
url(r'^(?P<version>[v1|v2]+)/parser/$', views.ParserView.as_view()),
b. 当继承ViewSetMixin后,路由在as_view中有了方法映射关系
url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list','post':'create'})),
c. 支持的路由格式(是渲染出来显示,还是纯json子字符串,不同的设置方式)
# http://127.0.0.1:8000/api/v1/v1/?format=json
url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list','post':'create'})),
# http://127.0.0.1:8000/api/v1/v1.json
url(r'^(?P<version>[v1|v2]+)/v1\.(?P<format>\w+)$', views.View1View.as_view({'get': 'list','post':'create'})),
url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)/$', views.View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)\.(?P<format>\w+)$', views.View1View.as_view({'get': 'retrieve','delete':'destroy','put':'update','patch':'partial_update'})),
d. 全自动路由(简单易用,单个情况最好自己写,包括所有基本的增删改查)
#在urls.py中
from api import views
from rest_framework import routers
# 会自动生成对应上边的四种路由形式
router = routers.DefaultRouter()
router.register(r'xxxxx', views.View1View) # 将视图函数注册进去
router.register(r'rt', views.View1View)
# 在urlpatterns中配置
urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/', include(router.urls)),
]
10. 渲染
为了看json数据的页面展示效果,基本只需要在
settings
里边配置上就行。
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class TestView(APIView):
# renderer_classes = [JSONRenderer,BrowsableAPIRenderer] # 平时只用这两个足够,设置加载配置文件中
def get(self, request, *args, **kwargs):
pass
REST_FRAMEWORK = {
"DEFAULT_RENDERER_CLASSES":[
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
]
}
当然也可以自己定制,根据类的继承查询界面的位置,找到原生界面修改界面代码,打自己公司的广告等。