Django REST framework
一、起步
由于笔者水平有限,学习技术官方文档永远是首先被推荐的,推荐来自Django REST framework官网的快速教程
二、序列化组件
models:
后面的代码将基于这几个表来做操作
1 class BookDetailView(APIView): 2 """ 3 针对单条数据的序列化 4 """ 5 6 def get(self,request,id): 7 8 book=Book.objects.filter(pk=id).first() 9 bs=BookModelSerializers(book,context={'request': request}) 10 return Response(bs.data) 11 12 def put(self,request,id): 13 book=Book.objects.filter(pk=id).first() 14 bs=BookModelSerializers(book,data=request.data) 15 if bs.is_valid(): 16 bs.save() 17 return Response(bs.data) 18 else: 19 return Response(bs.errors) 20 21 def delete(self,request,id): 22 Book.objects.filter(pk=id).delete() 23 24 return Response()
序列化的方法:
方法一: 硬传值
class PublishView(View): def get(self,request): # 方式一: publish_list = list(Publish.objects.all().values("name","email")) # {'name': '苹果出版社', 'email': '123@qq.com'}{'name': '橘子出版社', 'email': '456@qq.com'} return HttpResponse(publish_list)
方法二: model_to_dict(obj)
1 class PublishView(View): 2 def get(self,request): 3 from django.forms.models import model_to_dict 4 publish_list = Publish.objects.all() 5 temp = [] 6 for obj in publish_list: 7 # temp.append({ 8 # "name":obj.name, 9 # "email":obj.email 10 # }) 11 temp.append(model_to_dict(obj)) 12 print(temp, type(temp)) 13 # [{'name': '苹果出版社', 'email': '123@qq.com'}, {'name': '橘子出版社', 'email': '456@qq.com'}] <class 'list'> 14 # [{'id': 1, 'name': '苹果出版社', 'email': '123@qq.com'}, {'id': 2, 'name': '橘子出版社', 'email': '456@qq.com'}] <class 'list'> 15 16 return HttpResponse(temp)
方法三: serializers.serialize("json",publish_list)
class PublishView(View): def get(self,request): from django.core import serializers publish_list = Publish.objects.all() ret = serializers.serialize("json",publish_list) """ [ { "model": "app01.publish", "pk": 1, "fields": { "name": "苹果出版社", "email": "123@qq.com" } }, { "model": "app01.publish", "pk": 2, "fields": { "name": "橘子出版社", "email": "456@qq.com" } } ] """ return HttpResponse(ret)
方法四:rest_framework serializers
1 from rest_framework import serializers 2 3 class PublishSerializers(serializers.Serializer): 4 name = serializers.CharField() 5 email = serializers.EmailField() 6 7 8 class PublishView(View): 9 def get(self,request): 10 # 方式四(推荐) 11 publish_list = Publish.objects.all() 12 ret = PublishSerializers(publish_list, many=True) # queryset 13 # print(ret.data) 14 # print(type(ret.data)) 15 # [OrderedDict([('name', '苹果出版社'), ('email', '123@qq.com')]), 16 # OrderedDict([('name', '橘子出版社'), ('email', '456@qq.com')])] 17 # <class 'rest_framework.utils.serializer_helpers.ReturnList'> 18 """ 19 >>>dict([("name","橘子出版社"),("email","456@qq.com")]) 20 {'name': '橘子出版社', 'email': '456@qq.com'} 21 """ 22 23 return HttpResponse(ret.data)
终结者:ModelSerializer
我们的 PublishSerializers 类中重复了很多包含在Publish模型类(model)中的信息。如果能保证我们的代码整洁,那就更好了。
就像Django提供了Form
类和ModelForm
类一样,REST framework包括Serializer
类和ModelSerializer
类。
下面我们换一个稍微复杂的有外键的模型来进行演示:
1 class BookModelSerializers(serializers.ModelSerializer): 2 class Meta: 3 model = Book 4 fields = "__all__" 5 6 #publish=serializers.CharField(source="publish.pk") 7 publish=serializers.HyperlinkedIdentityField( 8 view_name="detailpublish", 9 lookup_field="publish_id", 10 lookup_url_kwarg="pk" 11 ) 12 13 14 class BookView(APIView): 15 def get(self,request): 16 book_list=Book.objects.all() 17 bs=BookModelSerializers(book_list,many=True,context={'request': request}) 18 return Response(bs.data) 19 def post(self,request): 20 # post请求的数据 21 bs=BookModelSerializers(data=request.data) 22 if bs.is_valid(): 23 print(bs.validated_data) 24 bs.save()# create方法 25 return Response(bs.data) 26 else: 27 return Response(bs.errors) 28 29 class BookDetailView(APIView): 30 """ 31 针对单条数据的序列化 32 """ 33 34 def get(self,request,id): 35 36 book=Book.objects.filter(pk=id).first() 37 bs=BookModelSerializers(book,context={'request': request}) 38 return Response(bs.data) 39 40 def put(self,request,id): 41 book=Book.objects.filter(pk=id).first() 42 bs=BookModelSerializers(book,data=request.data) 43 if bs.is_valid(): 44 bs.save() 45 return Response(bs.data) 46 else: 47 return Response(bs.errors) 48 49 def delete(self,request,id): 50 Book.objects.filter(pk=id).delete() 51 52 return Response()
三、APIVIEW
在CBV的基础上,视图类在继承了REST framework的 APIVIEW 后产生了新的调用方法
request的加强:
1 原生request支持的操作 2 print("POST",request.POST) 3 print("body",request.body) 4 # print(request) 5 print(type(request)) 6 from django.core.handlers.wsgi import WSGIRequest 7 新的request支持的操作 8 print("request.data",request.data) 9 print("request.data type",type(request.data))
四、认证和授权
a. 用户url传入的token认证
from django.conf.urls import url, include from web.viewsimport TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ]
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import BaseAuthentication from rest_framework.request import Request from rest_framework import exceptions token_list = [ 'sfsfss123kuf3j123', 'asijnfowerkkf9812', ] class TestAuthentication(BaseAuthentication): def authenticate(self, request): """ 用户认证,如果验证成功后返回元组: (用户,用户Token) :param request: :return: None,表示跳过该验证; 如果跳过了所有认证,默认用户和Token和使用配置文件进行设置 self._authenticator = None if api_settings.UNAUTHENTICATED_USER: self.user = api_settings.UNAUTHENTICATED_USER() else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN() else: self.auth = None (user,token)表示验证通过并设置用户名和Token; AuthenticationFailed异常 """ val = request.query_params.get('token') if val not in token_list: raise exceptions.AuthenticationFailed("用户认证失败") return ('登录用户', '用户token') def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ # 验证失败时,返回的响应头WWW-Authenticate对应的值 pass class TestView(APIView): authentication_classes = [TestAuthentication, ] permission_classes = [] def get(self, request, *args, **kwargs): print(request.user) print(request.auth) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs): return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容')
b. 请求头认证
from django.conf.urls import url, include from web.viewsimport TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ] urls.py
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import BaseAuthentication from rest_framework.request import Request from rest_framework import exceptions token_list = [ 'sfsfss123kuf3j123', 'asijnfowerkkf9812', ] class TestAuthentication(BaseAuthentication): def authenticate(self, request): """ 用户认证,如果验证成功后返回元组: (用户,用户Token) :param request: :return: None,表示跳过该验证; 如果跳过了所有认证,默认用户和Token和使用配置文件进行设置 self._authenticator = None if api_settings.UNAUTHENTICATED_USER: self.user = api_settings.UNAUTHENTICATED_USER() else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN() else: self.auth = None (user,token)表示验证通过并设置用户名和Token; AuthenticationFailed异常 """ import base64 auth = request.META.get('HTTP_AUTHORIZATION', b'') if auth: auth = auth.encode('utf-8') auth = auth.split() if not auth or auth[0].lower() != b'basic': raise exceptions.AuthenticationFailed('验证失败') if len(auth) != 2: raise exceptions.AuthenticationFailed('验证失败') username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':') if username == 'alex' and password == '123': return ('登录用户', '用户token') else: raise exceptions.AuthenticationFailed('用户名或密码错误') def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ return 'Basic realm=api' class TestView(APIView): authentication_classes = [TestAuthentication, ] permission_classes = [] def get(self, request, *args, **kwargs): print(request.user) print(request.auth) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs): return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容') views.py
c. 多个认证规则
from django.conf.urls import url, include from web.views.s2_auth import TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ]
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import BaseAuthentication from rest_framework.request import Request from rest_framework import exceptions token_list = [ 'sfsfss123kuf3j123', 'asijnfowerkkf9812', ] class Test1Authentication(BaseAuthentication): def authenticate(self, request): """ 用户认证,如果验证成功后返回元组: (用户,用户Token) :param request: :return: None,表示跳过该验证; 如果跳过了所有认证,默认用户和Token和使用配置文件进行设置 self._authenticator = None if api_settings.UNAUTHENTICATED_USER: self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户 else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None else: self.auth = None (user,token)表示验证通过并设置用户名和Token; AuthenticationFailed异常 """ import base64 auth = request.META.get('HTTP_AUTHORIZATION', b'') if auth: auth = auth.encode('utf-8') else: return None print(auth,'xxxx') auth = auth.split() if not auth or auth[0].lower() != b'basic': raise exceptions.AuthenticationFailed('验证失败') if len(auth) != 2: raise exceptions.AuthenticationFailed('验证失败') username, part, password = base64.b64decode(auth[1]).decode('utf-8').partition(':') if username == 'alex' and password == '123': return ('登录用户', '用户token') else: raise exceptions.AuthenticationFailed('用户名或密码错误') def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ # return 'Basic realm=api' pass class Test2Authentication(BaseAuthentication): def authenticate(self, request): """ 用户认证,如果验证成功后返回元组: (用户,用户Token) :param request: :return: None,表示跳过该验证; 如果跳过了所有认证,默认用户和Token和使用配置文件进行设置 self._authenticator = None if api_settings.UNAUTHENTICATED_USER: self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户 else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None else: self.auth = None (user,token)表示验证通过并设置用户名和Token; AuthenticationFailed异常 """ val = request.query_params.get('token') if val not in token_list: raise exceptions.AuthenticationFailed("用户认证失败") return ('登录用户', '用户token') def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ pass class TestView(APIView): authentication_classes = [Test1Authentication, Test2Authentication] permission_classes = [] def get(self, request, *args, **kwargs): print(request.user) print(request.auth) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs): return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容')
d. 认证和权限
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ]
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import BaseAuthentication from rest_framework.permissions import BasePermission from rest_framework.request import Request from rest_framework import exceptions token_list = [ 'sfsfss123kuf3j123', 'asijnfowerkkf9812', ] class TestAuthentication(BaseAuthentication): def authenticate(self, request): """ 用户认证,如果验证成功后返回元组: (用户,用户Token) :param request: :return: None,表示跳过该验证; 如果跳过了所有认证,默认用户和Token和使用配置文件进行设置 self._authenticator = None if api_settings.UNAUTHENTICATED_USER: self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户 else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None else: self.auth = None (user,token)表示验证通过并设置用户名和Token; AuthenticationFailed异常 """ val = request.query_params.get('token') if val not in token_list: raise exceptions.AuthenticationFailed("用户认证失败") return ('登录用户', '用户token') def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ pass class TestPermission(BasePermission): message = "权限验证失败" def has_permission(self, request, view): """ 判断是否有权限访问当前请求 Return `True` if permission is granted, `False` otherwise. :param request: :param view: :return: True有权限;False无权限 """ if request.user == "管理员": return True # GenericAPIView中get_object时调用 def has_object_permission(self, request, view, obj): """ 视图继承GenericAPIView,并在其中使用get_object时获取对象时,触发单独对象权限验证 Return `True` if permission is granted, `False` otherwise. :param request: :param view: :param obj: :return: True有权限;False无权限 """ if request.user == "管理员": return True class TestView(APIView): # 认证的动作是由request.user触发 authentication_classes = [TestAuthentication, ] # 权限 # 循环执行所有的权限 permission_classes = [TestPermission, ] def get(self, request, *args, **kwargs): # self.dispatch print(request.user) print(request.auth) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs): return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容') views.py
e. 全局使用
上述操作中均是对单独视图进行特殊配置,如果想要对全局进行配置,则需要再配置文件中写入即可。
REST_FRAMEWORK = { 'UNAUTHENTICATED_USER': None, 'UNAUTHENTICATED_TOKEN': None, "DEFAULT_AUTHENTICATION_CLASSES": [ "web.utils.TestAuthentication", ], "DEFAULT_PERMISSION_CLASSES": [ "web.utils.TestPermission", ], }
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ]
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_framework.response import Response class TestView(APIView): def get(self, request, *args, **kwargs): # self.dispatch print(request.user) print(request.auth) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs): return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容') views.py
五、用户访问次数/频率限制
a. 基于用户IP限制访问频率
b. 基于用户IP显示访问频率(利于Django缓存)
c. view中限制请求频率
d. 匿名时用IP限制+登录时用Token限制
e. 全局使用
六、解析器(parser)
根据请求头 content-type 选择对应的解析器就请求体内容进行处理。
a. 仅处理请求头content-type为application/json的请求体
b. 仅处理请求头content-type为application/x-www-form-urlencoded 的请求体
c. 仅处理请求头content-type为multipart/form-data的请求体
d. 仅上传文件
e. 同时多个Parser
当同时使用多个parser时,rest framework会根据请求头content-type自动进行比对,并使用对应parser
f. 全局使用
注意:个别特殊的值可以通过Django的request对象 request._request 来进行获取
七、分页
a. 根据页码进行分页
b. 位置和个数进行分页
c. 游标分页
八、路由系统
a. 自定义路由
b. 半自动路由
c. 全自动路由
九、视图
a. GenericViewSet
b. ModelViewSet(自定义URL)
c. ModelViewSet(rest framework路由)
参考资料:
武佩奇:https://www.cnblogs.com/wupeiqi/articles/7805382.htmlYUAN先生: https://www.cnblogs.com/yuanchenqi/articles/8719520.html
本文来自博客园,作者:YanceDev,转载请注明原文链接:https://www.cnblogs.com/yance-dev/p/9800552.html