通过上篇文章中我们讲到的序列化操作后。如果想让它通过在请求上传参数的形式给后台、后台获取到参数通过数据库limit的方式获取数据
,这种方法是可以实现的。
如果不想通过自己写的方法来实现分页,可以在rest framework框架中内置的PageNumberPagination类实现,该类中有多个对象需要传递,如下:
每一页显示多少条数据,可在settings配置中设置或自己自动配置
)默认为None
)默认为None
)默认通过?page=页码进行翻页
)此时设置settings配置文件,将其设置为每一页显示2条数据,并配置上rest framework。
settings如下:
# 配置上rest framework。
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api.apps.ApiConfig',
'rest_framework',
]
REST_FRAMEWORK = {
"DEFAULT_VERSION": 'v1',
"ALLOWED_VERSIONS": ['v1', 'v2'],
"VERSION_PARAM": 'version',
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
"PAGE_SIZE": 2,
}
urls.py如下:
from django.urls import path, re_path
from api import views
urlpatterns = [
re_path(r'api/(?P[v1|v2]+)/pager1/$' , views.Pager1View.as_view()),
]
views.py如下:
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination
class PagerSerialiser(serializers.ModelSerializer):
class Meta:
model = models.Role
fields = "__all__"
class MyPagination(PageNumberPagination):
page_size = 2
# 默认显示多少个数据
page_size_query_param = 'size'
# 最大显示多少个数据
max_page_size = 4
page_query_param = 'page'
class Pager1View(APIView):
def get(self, request, *args, **kwargs):
# 获取数据库数据
roles = models.Role.objects.all()
# 实例化分页操作
pg = MyPagination()
pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)
# 序列化操作
ser = PagerSerialiser(instance=pager_roles, many=True)
return Response(ser.data)
可以看到,此时我们定义了三个类,每个类都有不同的分工,通过序列化获取Role表的所有对象、以及继承于PageNumberPagination类的子类,重写该父类类方法、和访问接口路由,实例化分页、在将其序列化并返回给ser,之后在调用data函数解析数据
并返回通过rest framework框架内置的Response方法将其转换成rest framework的好看界面、以及帮我们转为json格式
。
此时访问http://127.0.0.1:8000/api/v1/pager1/如下:
可以看到通过?page=页数(默认
),是可以分页访问的,且如果想在后面加其他限制可通过&size=页数重写每页数据(在MyPagination类中重写了page_size_query_param=“size”、使其生效
),也可以通过MyPagination类显示了最大显示数据值。
如果返回的参数为PageNumberPagination类中的get_paginated_response函数,那么返回的结果就会显示上一条和下一条的路由(没有数据了则显示NULL
)。
views.py/Pager1View类如下:
class Pager1View(APIView):
def get(self, request, *args, **kwargs):
# 获取数据库数据
roles = models.Role.objects.all()
# 实例化分页操作
pg = MyPagination()
pager_roles = pg.paginate_queryset(queryset=roles, request=request, view=self)
# 序列化操作
ser = PagerSerialiser(instance=pager_roles, many=True)
return pg.get_paginated_response(ser.data)
此时访问http://127.0.0.1:8000/api/v1/pager1/如下:
可以看到上一条和下一条的路由也被放入到数据中了(实际用哪个返回根据需求做决定
)。
对于rest framework框架中还有另一个内置的LimitOffsetPagination类,该类中有多个对象需要传递,如下:
从0开始索引,默认使用offset做key
)数据显示多少条,没写用全局配置的两条,默认使用limit做key
)最大多少条每一页,默认为None
)没写从配置文件找
)views.py/MyPagination类如下:
from rest_framework.pagination import LimitOffsetPagination
class MyPagination(LimitOffsetPagination):
default_limit = 2
limit_query_param = 'limit'
offset_query_param = 'offset'
max_limit = 4
此时访问http://127.0.0.1:8000/api/v1/pager1/如下:
显然和上面效果一样,不过是传递的参数不同,不过内部实现原理也是不相同的。
对于rest framework框架中还有另一个内置的CursorPagination类,该类中将分页的数据加密了,具体的数据参数如下:
翻页数据的key,默认为cursor
)通过数据中什么字段降序、升序
)自定义每页显示多少个,默认为None
)最大每页长度数据,默认为None
)没有去配置文件获取
)views.py/MyPagination类如下:
from rest_framework.pagination import CursorPagination
class MyPagination(CursorPagination):
cursor_query_param = 'cursor'
page_size = 2
ordering = 'id'
page_size_query_param = 'size'
max_page_size = 4
此时访问http://127.0.0.1:8000/api/v1/pager1/如下:
可以看到,和上面的那些内置类相同,不过是多了对每页显示页数的页码进行了加密,且内部源码中是通过id的值进行索引判断的,所以不会造成当从一个很小的数跳到很大的数时,向数据库发送的索引范围会很大。
到现在为止,我们的视图一直继承着rest framework框架中的APIVIew,而APIVIew是继承于View的
,那么在DRF框架中,还有内置帮我们实现好的类,并且该类继承于APIView。
GenericAPIView类继承于APIVIew,功能更多,可实现分页,序列化、数据获取,不过该方法的使用有好有坏:
视图上减少代码量,便于使用
)要想使用该类,需满足该类所需的参数传递如下:
urls.py如下:
from django.urls import path, re_path
from api import views
urlpatterns = [
path('user/', views.UserInfoView.as_view()),
re_path(r'user/(?P[v1|v2]+)/group/(?P\d+)$' , views.GroupView.as_view(), name='gp'),
re_path(r'user/(?P[v1|v2]+)/group/$' , views.UserGroupView.as_view()),
re_path(r'api/(?P[v1|v2]+)/pager1/$' , views.Pager1View.as_view()),
re_path(r'api/(?P[v1|v2]+)/v1/$' , views.View1View.as_view()),
]
views.py如下:
from rest_framework import serializers
from api import models
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
from rest_framework.generics import GenericAPIView
class PagerSerialiser(serializers.ModelSerializer):
class Meta:
model = models.Role
fields = "__all__"
class View1View(GenericAPIView):
queryset = models.Role.objects.all()
serializer_class = PagerSerialiser
pagination_class = PageNumberPagination
def get(self, request, *args, **kwargs):
# 获取数据
roles = self.get_queryset()
# 分页
pager_roles = self.paginate_queryset(roles)
# 序列化
ser = self.get_serializer(instance=pager_roles, many=True)
return Response(ser.data)
此时我们可以看到,在传入serializer_class 、pagination_class的时候传的类无需传括号。
是因为在调用paginate_queryset、get_serializer函数的时候,内部会调用并获取相应的值、序列化操作。
此时访问http://127.0.0.1:8000/api/v1/v1/显示:
可以看到,通过这几步骤就将分页、序列化、解析的功能都给实现了。
仅仅的一些功能,对于现在的我们而言是根本不够的,此时DRF框架还有内置类供我们使用,而该方法为GenericViewSet类,继承于ViewSetMixin类(该类的调用方法重写了View方法中的as_view方法,使得后面继承类可以通过请求定义其他别名,且定义相应的函数
)、GenericAPIView类。
且在使用了该类后,路由中的as_view方法就需要定义参数了,具体如下:
urls.py如下:
from django.urls import path, re_path
from api import views
urlpatterns = [
path('user/', views.UserInfoView.as_view()),
re_path(r'user/(?P[v1|v2]+)/group/(?P\d+)$' , views.GroupView.as_view(), name='gp'),
re_path(r'user/(?P[v1|v2]+)/group/$' , views.UserGroupView.as_view()),
re_path(r'api/(?P[v1|v2]+)/pager1/$' , views.Pager1View.as_view()),
re_path(r'api/(?P[v1|v2]+)/v1/$' , views.View1View.as_view({
'get': 'list'})),
]
views.py/View1View函数如下:
from rest_framework import serializers
from api import models
from rest_framework.response import Response
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
from rest_framework.generics import GenericViewSet
class PagerSerialiser(serializers.ModelSerializer):
class Meta:
model = models.Role
fields = "__all__"
class View1View(GenericViewSet):
queryset = models.Role.objects.all()
serializer_class = PagerSerialiser
pagination_class = PageNumberPagination
def list(self, request, *args, **kwargs):
# 获取数据
roles = self.get_queryset()
# 分页
pager_roles = self.paginate_queryset(roles)
# 序列化
ser = self.get_serializer(instance=pager_roles, many=True)
return Response(ser.data)
可以发现通过GenericViewSet类继承的ViewSetMixin类中重写了as_view方法,且请求函数发生了改变(可以自定义函数名
)。
此时访问http://127.0.0.1:8000/api/v1/v1/显示:
此时访问的结果和上述GenericAPIView类是一致的。
通过上面GenericViewSet类中,我们发现请求函数名发生了修改,那么为什么要修改?修改的作用是什么呢?
此时就要说到DRF框架中的ModelViewSet类了,该类继承的父类有很多,包括以GenericViewSet类开头等:
而这些内置类,我们若想使用,仅仅只需要通过配置url中的as_view方法即可。
urls.py如下:
from django.urls import path, re_path
from api import views
urlpatterns = [
re_path(r'api/(?P[v1|v2]+)/v1/(?P\d+)/$' ,
views.View1View.as_view({
'get': 'retrieve', 'post': 'create', 'delete': 'destroy', 'put': 'update',
'patch': 'partial_update'})),
]
可以看到,每一个请求函数都有相关内置封装的类。
views.py如下:
from rest_framework.viewsets import ModelViewSet
from rest_framework.pagination import PageNumberPagination
class PagerSerialiser(serializers.ModelSerializer):
class Meta:
model = models.Role
fields = "__all__"
class View1View(ModelViewSet):
queryset = models.Role.objects.all()
serializer_class = PagerSerialiser
pagination_class = PageNumberPagination
此时访问http://127.0.0.1:8000/api/v1/v1/1/如下:
可以看到只需要几步骤就可以完成数据的增删改查了,在使用的过程中,我们可根据自身需求来自定义添加继承的对象。
通过上面的路由访问中,发现如果我们前端想获取到数据的时候不可能是以渲染成rest framework的形式返回过去,我们需要json格式,在前面测试的时候点击了一个format参数那时候路由后面添加了一个?format=json,此时格式就变为json了
,这是第一种方法。
而还有一种方法是通过路由的形式来正则表达式,使其通过后缀.json即可获取到json格式数据。
urls.py如下:
from django.urls import path, re_path
from api import views
urlpatterns = [
re_path(r'api/(?P[v1|v2]+)/v1\.(?P\w+)$' ,
views.View1View.as_view({
'get': 'list', 'post': 'create'})),
]
此时访问http://127.0.0.1:8000/api/v1/v1.json如下:
可以看到通过定义路由的方法使数据变成了json的格式。
可以看到如果一个视图展示、一个视图转换成json的格式,浪费路由,且代码还很多,而这个问题rest framework框架也帮我们想到了,使用的时候调用其routers类,将视图作为参数通过include导入即可。
urls.py如下:
from django.urls import path, re_path, include
from api import views
from rest_framework import routers
router = routers.DefaultRouter()
router.register(r'rt', views.View1View)
urlpatterns = [
re_path(r'api/(?P[v1|v2]+)/' , include(router.urls)),
]
此时我们访问http://127.0.0.1:8000/api/v1/rt/如下:
可以看到该方法将我们所有的路由都整合起来了,此时的代码变得很十分的简洁、且json格式和rest framework视图随意切换、非常灵活。
根据前面的rest framework提供给我们的界面,我们在想这到底是通过什么来展示出来的呢?
其实是在我们继承APIVIew的同时调用了全局配置的renderer_classes,而参数为列表,默认将渲染好的界面,通过封装类、模板提供给我们展示了。
使用后通过?format=admin展示
)urls.py如下:
from django.urls import path, re_path, include
from api import views
urlpatterns = [
path('user/', views.UserInfoView.as_view()),
]
views.py如下:
from django.shortcuts import render, HttpResponse
from rest_framework import serializers
from rest_framework.views import APIView
from api import models
from rest_framework.response import Response
from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer, AdminRenderer
class UserInfoModelSerializer(serializers.ModelSerializer):
group = serializers.HyperlinkedIdentityField(view_name='gp', lookup_field='group_id', lookup_url_kwarg='pk')
class Meta:
model = models.UserInfo
fields = "__all__"
# fields = ['id', 'username', 'password', 'group', 'roles']
depth = 1
class UserInfoView(APIView):
renderer_classes = [JSONRenderer, BrowsableAPIRenderer]
def get(self, request, *args, **kwargs):
users = models.UserInfo.objects.all()
ser = UserInfoModelSerializer(instance=users, many=True, context={
'request': request})
return Response(ser.data)
此时访问http://127.0.0.1:8000/user/如下:
可以发现函数都被调用了,但是在一般情况下,我们只需要JSONRenderer, BrowsableAPIRenderer就够了,所以我们可以通过配置全局样式让它生效即可。
settings.py如下:
REST_FRAMEWORK = {
"DEFAULT_VERSION": 'v1',
"ALLOWED_VERSIONS": ['v1', 'v2'],
"VERSION_PARAM": 'version',
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",
"PAGE_SIZE": 2,
"DEFAULT_RENDERER_CLASSES": [
"rest_framework.renderers.JSONRenderer",
"rest_framework.renderers.BrowsableAPIRenderer"
]
}
此时全局的配置都应用上了,那么如果我们想自己改这个rest framework给我们的模板,我们可以通过查看BrowsableAPIRenderer类的源码,找到模板的位置并修改即可。