Django RestFramework常用组件笔记
知识点预览:
- 注:本文适合有一定Django基础的同学shiyong
- 注:本文主要用来复习restframework相关知识
- 如果有什么重要的内容,我会补上去。
总体说明
-
我们直接打开rest_framework.views文件源码,可以看到它包含一个类
APIView
,而这个类也基本包含了rest_framework
所封装的组件了。也就是说我们的学习目标就是下面这些组件的功能、以及如何配置。... ... class APIView(View): # The following policies may be set at either globally, or per-view. renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES parser_classes = api_settings.DEFAULT_PARSER_CLASSES authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS metadata_class = api_settings.DEFAULT_METADATA_CLASS versioning_class = api_settings.DEFAULT_VERSIONING_CLASS ... ...
-
实际上,上面展示的类库我并不建议从上往下看,我们列出这些类的功能:
renderer_classes
渲染器parser_classes
解析器authentication_classes
认证器throttle_classes
限流器permission_classes
权限content_negotiation_class
依据contentType选择渲染器。metadata_class
元数据versioning_class
版本
-
这些类的使用方法都可以由我们自行配置,例如
authentication_classes
认证器我们可以看到,他会去api_settings.DEFAULT_AUTHENTICATION_CLASSES
找到默认配置。于是我们就可以为他添加默认配置,打开setting文件添加以下配置:REST_FRAMEWORK={ 'DEFAULT_AUTHENTICATION_CLASSES':['api2.utils.auth.FirstAuthtication', 'api2.utils.auth.Authtication'] }
这样一来,就可以使用我们自己设置的权限认证器。具体逻辑需要在
api2.utils.auth.FirstAuthtication
去编写代码。
所有的DEFAULT_XXX_....
就都可在此处配置默认值。
认证、权限、限流组件
- 刚开始,我们从认证、权限、限流开始学习。
authentication_classes
认证器permission_classes
权限throttle_classes
限流器
authentication_classes
认证器BasicAuthentication
是django rest framework为我们提供了最基本的认证类,该类中其中定义的两个方法authenticate和authenticate_header(认证失败返回的响应头),使用时候重写该两个方法进行认证
要启用全局配置就需要把他放进我上面提到的全局配置属性class Authtication(BasicAuthentication): def authenticate(self,req): token=req._request.GET.get('token') token_obj=models.UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthticationFailed('用户认证失败') return (token_obj.user,token_obj) def authenticate_header(self,req): return 'Basic realm="api"'
REST_FRAMEWORK={}
中rest_framework.authentication
还提供了其他类BasicAuthentication #基于浏览器进行认证,浏览器弹框 SessionAuthentication #基于django的session进行认证 RemoteUserAuthentication #基于django admin中的用户进行认证,这也是官网的示例 TokenAuthentication #基于drf内部的token认证
permission_classes
权限,使用方法和认证类几乎是一样的。- 继承
BasePermission
类
如果已经使用了全局配置我们可以自定义CBV视图函数中的认证和权限了。from rest_framework.permissions import BasePermission class MyPermission(BasePermission): message="必须是SSVIP才能访问!" def has_permission(self,request,view): if request.user.user_type!='ssvip': return False return True class MyPermission2(BasePermission): def has_permission(self,request,view): if request.user.user_type=='ssvip': return False return True
class VIPOrderView(APIView): authentication_classes = [Authtication, ] #认证,不需要认证比如登录操作这里必须写[] permission_classes = [MyPermission2, ] #权限 def get(self, req, *args, **kwargs): ret = {'code': 1000, 'msg': None, 'data': None} ret['data'] = {'vip资源': {'a': 1, 'b': 2}} return JsonResponse(ret)
- 继承
throttle_classes
限流器,使用方法几乎和上面两种一样,仍是以列表形式在CBV视图函数中配置,或是在全局配置。打开rest_framework.throttling
里面有个SimpleRateThrottle
类,可以发现它读取了配置DEFAULT_THROTTLE_RATES
。那么可发现在全局setting中可以配置默认访问频率规则。而DEFAULT_THROTTLE_CLASSES
的配置类仅仅是返回了被限制用户的对象。SimpleRateThrottle
继承内置的限流类。
然后我们可以在setting全局配置里面配置我们的访问频率、已经默认使用的规则:# 未登录用户 class UserThrottle(SimpleRateThrottle): scope = "user" def get_cache_key(self, request, view): return self.get_ident(request) # vip 用户 class VipThrottle(SimpleRateThrottle): scope = "vip" def get_cache_key(self, request, view): return request.user.username
可以看到,vip用户10次每分钟,普通用户5次每分钟。我注释掉的内容就是之前配置的默认认证类和权限类。如果不想使用默认配置 我们就得去CBV函数中自行配置。例如,我希望配置vip用户规则REST_FRAMEWORK={ #'DEFAULT_AUTHENTICATION_CLASSES':['api2.utils.auth.FirstAuthtication', #'api2.utils.auth.Authtication'], #'DEFAULT_PERMISSION_CLASSES':['api2.utils.Permission.MyPermission', #'api2.utils.Permission.MyPermission2',], 'DEFAULT_THROTTLE_CLASSES':['api2.utils.throttle.VisitThrottle'], 'DEFAULT_THROTTLE_RATES':{ "vip":"10/m", "user":"5/m", } }
throttle_classes = [VipThrottle,]
class VIPOrderView(APIView): authentication_classes = [Authtication, ] permission_classes = [MyPermission2, ] throttle_classes = [VipThrottle,] def get(self, req, *args, **kwargs): ret = {'code': 1000, 'msg': None, 'data': None} ret['data'] = {'vip资源': {'a': 1, 'b': 2}} return JsonResponse(ret)
版本、解析器
versioning_class
版本信息。也就是url中的参数信息获取。
然后我们就可以在url里面填写版本信息了。from rest_framework.versioning import URLPathVersioning class UserView(APIView): authentication_classes = [] permission_classes = [] versioning_class = URLPathVersioning #ParamVersion也可以自己定义一个这样的函数,如下 def get(self, req, *args, **kwargs): print(req.version) return HttpResponse('用戶列表') #也选择自己定义一个这样的函数,url后加?version=1.1 class ParamVersion(object): def dermine_version(self,req, *args, **kwargs): version=req.query_params.get('version') return version
仍然可以在全局配置urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/users/$',views.UserView.as_view(),name='aaa'), ]
'DEFAULT_VERSIONING_CLASS':"rest_framework.versioning.URLPathVersioning"
。我们直接使用内置类,和上面不同的是我们根本没有继承,而是直接使用了框架为我们提供的。- url反向生成:
class UserView(APIView): authentication_classes = [] permission_classes = [] versioning_class = URLPathVersioning # ParamVersion def get(self, req, *args, **kwargs): print(req.version) #打印参数url参数 ul = req.versioning_scheme.reverse(viewname='aaa', request=req) #url中找到name属性的路径。 print(ul) #打印反向生成的url return HttpResponse('用戶列表')
- 它还提供了其他内置版本类:
class BaseVersioning class AcceptHeaderVersioning(BaseVersioning) class NamespaceVersioning(BaseVersioning) class HostNameVersioning(BaseVersioning) class QueryParameterVersioning(BaseVersioning)
- url反向生成:
parser_classes
解析器,用来自动解析数据例如json格式数据,当然也包括其他格式。也内置了很多类。我们看个例子
上面使用了两个解析器from rest_framework.parsers import JSONParser,FormParser class parserView(APIView): parser_classes = [JSONParser,FormParser,] #使用解析器 def post(self,req,*args,**kwargs): print(req.data) #不再使用json.loads(req.body)了 return HttpResponse('parserView')
JSONParser
可以解析application/json
请求头。FormParser
可以解析application/x-www-form-urlencoded
请求头。- 同样,框架包含了很多我们可以直接使用的内置类。
我们需要文件上传时就要在CBV写上:class MultiPartParser(BaseParser) #支撑'multipart/form-data' class FileUploadParser(BaseParser) #media_type = '*/*'支持全部类型。
parser_classes = [FileUploadParser,]
- 同样,框架包含了很多我们可以直接使用的内置类。
序列化、用序列化验证post请求
- 引入部分,了解一下就好
rest_framework.serializers.Serializer
。from rest_framework import serializers class RoleSerializer(serializers.Serializer): id=serializers.IntegerField() title = serializers.CharField() import json class rolesView(APIView): authentication_classes = [] permission_classes = [] versioning_class = URLPathVersioning # ParamVersion def get(self, req, *args, **kwargs): roles = models.Role.objects.all() ser = RoleSerializer(instance=roles, many=True) ret = json.dumps(ser.data,ensure_ascii=False) return HttpResponse(ret) #不使用框架时候的样子 import json class rolesView_0(APIView): def get(self, req, *args, **kwargs): roles = models.Role.objects.all().values('id', 'title') roles = list(roles) ret = json.dumps(roles) return HttpResponse(ret) #没有解码。
- 定制序列化内容,仍然是了解一下就好,因为框架帮我们做好了封装。下面是一个用户对应一个组,对应多个角色。
class UserinfoSerializer(serializers.Serializer): xxx=serializers.CharField(source="user_type") username=serializers.CharField() password=serializers.CharField() gp=serializers.CharField(source="group.title") rls=serializers.SerializerMethodField(source="roles.all") #自行定制! def get_rls(self,row): role_obj_list=row.roles.all() ret=[] for i in role_obj_list: ret.append({'id':i.id,'title':i.title}) return ret class Userinfo(APIView): def get(self, req, *args, **kwargs): users=models.UserInfo.objects.all() ser=UserinfoSerializer(instance=users,many=True) ret=json.dumps(ser.data,ensure_ascii=False) return HttpResponse(ret)
- Model序列化可以继承
ModelSerializer
非常重要 ,但是代码量非常少~,用于大多情况。也可以选择定制。class modUserinfoSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = "__all__" #fields=['id','username','password','rls'] #depth=1 #外鍵層數,可以展开。 #rls=serializers.SerializerMethodField() def get_rls(self,row): #也可以定制 pass class Userinfo(APIView): def get(self, req, *args, **kwargs): users = models.UserInfo.objects.all() ser = modUserinfoSerializer(instance=users, many=True) ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret)
- 还有点小操作,在返回的json中,反向生成url,科用于分页。
class modUserinfoSerializer(serializers.ModelSerializer): group=serializers.HyperlinkedIdentityField(view_name='gp') class Meta: model = models.UserInfo fields = "__all__" class Userinfo(APIView): def get(self, req, *args, **kwargs): users = models.UserInfo.objects.all() ser = modUserinfoSerializer(instance=users, many=True,context={'request':req}) #必须 ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret)
- 还有点小操作,在返回的json中,反向生成url,科用于分页。
- 用序列化验证用户请求
class GroupSerializer(serializers.Serializer): title=serializers.CharField(error_messages={'required':'title不能为空'}) name=serializers.CharField(error_messages={'required':'name不能为空'}) class groupView(APIView): def post(self,req,*args,**kwargs): ser=GroupSerializer(data=req.data) if ser.is_valid(): print(ser.validated_data) else: print(ser.errors) return HttpResponse('提交数据')
分页
PageNumberPagination
分页类的基本使用(了解即可)
还没完我们打开这个类的源码,发现他有个#分页需要序列化 from rest_framework.response import Response #渲染器 from rest_framework.pagination import PageNumberPagination #分页类 class PagerSerialiser(serializers.ModelSerializer): class Meta: model=models.Role fields = "__all__" class pager1View(APIView): def get(self, req, *args, **kwargs): #获取所有数据 roles=models.Role.objects.all() #创建分页对象、在数据库中获取分页数据 pa=PageNumberPagination() pager_roles=pa.paginate_queryset(queryset=roles,request=req) #序列化分页后的数据 ser=PagerSerialiser(instance=pager_roles,many=True) return Response(ser.data)
page_size = api_settings.PAGE_SIZE
属性。即默认分页大小,我们这时就要去setting里配置全局分页PAGE_SIZE
大小。我给每页显示两条数据。就是看最下面那行就可以,前几行是之前配置的其他默认类:REST_FRAMEWORK={ 'DEFAULT_AUTHENTICATION_CLASSES':['api2.utils.auth.FirstAuthtication', 'api2.utils.auth.Authtication'], 'DEFAULT_PERMISSION_CLASSES':['api2.utils.Permission.MyPermission', 'api2.utils.Permission.MyPermission2',], 'DEFAULT_THROTTLE_CLASSES':['api2.utils.throttle.UserThrottle', 'api2.utils.throttle.VipThrottle',], 'DEFAULT_THROTTLE_RATES':{ "vip":"10/m", "user":"5/m", }, "PAGE_SIZE":2, }
PageNumberPagination
类的继承-自定制显示内容,实际上我们打开这个类就可以看到那些我们需要自行定制的属性。我举个简单例子。
还可以自动生成上一页下一页的url地址到response中。class MyPageNumberPagination(PageNumberPagination): #继承定制分页 page_size=4 page_size_query_param = 'size' max_page_size = 5 page_query_param = 'page' class pager1View(APIView): def get(self, req, *args, **kwargs): ... ... pa=MyPageNumberPagination() #使用分页对象 ... ...
效果class pager1View(APIView): def get(self, req, *args, **kwargs): ... ... #return Response(ser.data) return pa.get_paginated_response(ser.data)
这样的分类策略只返回页数。非常有规律的分成了N也,要哪也数据写哪页就好。而下面在看一种分页策略。{ "count": 10, "next": "http://127.0.0.1:8000/api/v1/pager1/?page=3", "previous": "http://127.0.0.1:8000/api/v1/pager1/", "results": [ { "id": 5, "title": "无业" }, { "id": 6, "title": "阿毛" } ] }
LimitOffsetPagination
继承及使用,这个就比较常规了,就像我们数据库里面分页一样,他添加了offset参数。没事的话看看一看源码,里面都有配置。
效果:class MyPageNumberPagination2(LimitOffsetPagination): default_limit=2 limit_query_param='limit' offset_query_param='offset' max_limit = 5 class pager1View(APIView): def get(self, req, *args, **kwargs): #获取所有数据 roles=models.Role.objects.all() #创建分页对象、在数据库中获取分页数据 pa=MyPageNumberPagination2() pager_roles=pa.paginate_queryset(queryset=roles,request=req) #序列化分页后的数据 ser=PagerSerialiser(instance=pager_roles,many=True) #return Response(ser.data) return pa.get_paginated_response(ser.data)
这样的分页策略仍然是有规律的,如果我们需要只有在本页才能看到上一页和下一页路径则可以使用以下策略。{ "count": 10, "next": "http://127.0.0.1:8000/api/v1/pager1/?limit=2&offset=4", "previous": "http://127.0.0.1:8000/api/v1/pager1/?limit=2", "results": [ { "id": 3, "title": "警察" }, { "id": 4, "title": "小姐" } ] }
CursorPagination
根据游标分页。继承这个类,看一下源码,配置他的可配置项。
看一下效果,达成了游标位置无法判断效果。只有在本页才能看到下一页和上一页数据,性能最高:class MyPageNumberPagination3(CursorPagination): cursor_query_param = 'cursor' page_size = 2 ordering = 'id' class pager1View(APIView): def get(self, req, *args, **kwargs): ... ... pa=MyPageNumberPagination3() ... ... return pa.get_paginated_response(ser.data)
{ "next": "http://127.0.0.1:8000/api/v1/pager1/?cursor=cD00", "previous": "http://127.0.0.1:8000/api/v1/pager1/?cursor=cj0xJnA9Mw%3D%3D", "results": [ { "id": 3, "title": "警察" }, { "id": 4, "title": "小姐" } ] }
视图
GenericAPIView
(了解)如果你看了之前的内容,到了这一步会发现,视图函数这么简单就能完成。之间我们继承了APIView
,如今我们继承GenericAPIView
,打开源码发现它帮我们继承了APIView
并丰富了相当多的功能,我们只需要配置下一下就能完成复杂的操作。看个查询、分页、序列化后的接口实例:
只需要配置一下分页、序列化所用类,from rest_framework.generics import GenericAPIView class view1View(GenericAPIView): queryset = models.Role.objects.all() #查询全部数据 serializer_class = PagerSerialiser #序列化类 pagination_class = PageNumberPagination #分页类 def get(self, req, *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)
GenericAPIView
就能帮我们完成操作。但是他还可以被继承。GenericViewSet
他继承了ViewSetMixin
,和generics.GenericAPIView
,因为继承了ViewSetMixin
所以需要给as_view函数参入参数,帮我们把请求METHOD转化为类的方法。urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/v1/$',views.view1View.as_view({'get':'list'}),name='v1',), ] #url.py :{'get':'list'}将GET方法映射到list()上。
class view1View(GenericViewSet): queryset = models.Role.objects.all() #查询全部数据 serializer_class = PagerSerialiser #序列化类 pagination_class = PageNumberPagination #分页类 def list(self, req, *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)
- 重要:有了以上的基础之后我们视图函数就要飞升了。
class view1View(ListModelMixin,GenericViewSet,CreateModelMixin): queryset = models.Role.objects.all() #查询全部数据 serializer_class = PagerSerialiser #序列化类 pagination_class = PageNumberPagination #分页类
url(r'^(?P<version>[v1|v2]+)/v1/$',views.view1View.as_view({'get':'list','post':'create'}),name='v1',), url(r'^(?P<version>[v1|v2]+)/v1/(?P<pk>\d+)$',views.view1View.as_view({'get':'retrieve','delete':'destroy','put':'update','patch':'partial_update'}),name='v1',),
- 总结视图函数如何写?
- 增删改查:
ModelViewSet
增删:CreateModelMixin
,DestroyModelMixin
,GenericViewSet
逻辑复杂:GenericViewSet
或APIView
- 增删改查:
- 总结视图函数如何写?
路由、渲染
-
路由
rest_framework.routers
类,自动帮我们生成路由。from django.conf.urls import url,include from rest_framework import routers router=routers.DefaultRouter() router.register(r'xxxx',views.view1View) router.register(r'rt',views.view2View) #假如这个视图函数继承了ModelViewSet,即增删改查。 urlpatterns = [ url(r'^',include(router.urls)), ]
自动帮我们生成以下路由,
http://127.0.0.1:8000/api/rt/
分页查询全部,http://127.0.0.1:8000/api/rt/1/
查询ID为1的数据。http://127.0.0.1:8000/api/rt.json
或http://127.0.0.1:8000/api/rt/?format=json
以json形式返回:^api/ ^ ^xxxx/$ [name='role-list'] ^api/ ^ ^xxxx\.(?P<format>[a-z0-9]+)/?$ [name='role-list'] #直接生成4个路由! ^api/ ^ ^rt/$ [name='role-list'] ^api/ ^ ^rt\.(?P<format>[a-z0-9]+)/?$ [name='role-list'] ^api/ ^ ^rt/(?P<pk>[^/.]+)/$ [name='role-detail'] ^api/ ^ ^rt/(?P<pk>[^/.]+)\.(?P<format>[a-z0-9]+)/?$ [name='role-detail']
-
JSONRenderer
渲染器 在rest_framework.renderers
类,这个类包含了很多渲染器。我们直接访问http://127.0.0.1:8000/api/rt/?format=json
会调用这个渲染器。实际上,我们网页打开http://127.0.0.1:8000/api/rt/
并不是json数据,而是一个漂亮接口交互界面调用了渲染器BrowsableAPIRenderer
的渲染结果。
要是想不渲染直接返回JSON需要在视图函数中配置:class view2View(ModelViewSet): renderer_classes = [JSONRenderer,] #仅仅使用JSON渲染器 #之前还学了 authentication_classes = [] #不使用认证 permission_classes = [] #不使用权限 versioning_class = [] #不版本 throttle_classes = [] #不使用限流 queryset = models.Role.objects.all() #查询全部数据 serializer_class = PagerSerialiser #序列化类 pagination_class = PageNumberPagination #分页类
当然也可以在全局配置
DEFAULT_RENDERER_CLASSES
来配配置全局渲染器。
Reference:
https://www.cnblogs.com/honey-badger/p/9740207.html
https://www.bbsmax.com/A/rV57PkAXdP/