1. 开发模式 - 普通开发方式(前后端放在一起写) - 前后端分离 2. 后端开发 为前端提供URL(API/接口的开发) 注:永远返回HttpResponse
Django的FBV和CBV
FBV :function base view
def users(request): user_list = ['alex','oldboy'] return HttpResponse(json.dumps((user_list)),status=200) #状态码
CBV:class base view
路由: url(r'^students/', views.StudentsView.as_view()), 试图: #根据请求方式不同自动调用相应的方法 from django.views import View class StudentsView(View): def get(self,request,*args,**kwargs): return HttpResponse('GET',status=200) def post(self, request, *args, **kwargs): return HttpResponse('POST',status=200) def put(self, request, *args, **kwargs): return HttpResponse('PUT',status=200) def delete(self, request, *args, **kwargs): return HttpResponse('DELETE',status=200)
原理:
CBV,基于反射实现根据请求方式不同,执行不同的方法。 url -> view方法 -> dispatch方法(反射执行其他:GET/POST/DELETE/PUT)
流程:
流程: class StudentsView(View): def dispatch(self, request, *args, **kwargs): print('before') ret = super(StudentsView,self).dispatch(request, *args, **kwargs) print('after') return ret def get(self,request,*args,**kwargs): return HttpResponse('GET') def post(self, request, *args, **kwargs): return HttpResponse('POST') def put(self, request, *args, **kwargs): return HttpResponse('PUT') def delete(self, request, *args, **kwargs): return HttpResponse('DELETE')
继承:多个类共用的功能,为了避免重复编写
继承(多个类共用的功能,为了避免重复编写): from django.views import View class MyBaseView(object): def dispatch(self, request, *args, **kwargs): print('before') ret = super(MyBaseView,self).dispatch(request, *args, **kwargs) print('after') return ret class StudentsView(MyBaseView,View): def get(self,request,*args,**kwargs): print('get方法') return HttpResponse('GET') def post(self, request, *args, **kwargs): return HttpResponse('POST') def put(self, request, *args, **kwargs): return HttpResponse('PUT') def delete(self, request, *args, **kwargs): return HttpResponse('DELETE') class TeachersView(MyBaseView,View): def get(self,request,*args,**kwargs): return HttpResponse('GET') def post(self, request, *args, **kwargs): return HttpResponse('POST') def put(self, request, *args, **kwargs): return HttpResponse('PUT') def delete(self, request, *args, **kwargs): return HttpResponse('DELETE')
什么是RESTful
REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移” REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)
restful API设计 规范
1. API与用户的通信协议,总是使用HTTPs协议。 2.域名 https://api.example.com 尽量将API部署在专用域名(会存在跨域问题) https://example.org/api/ API很简单 3.版本 URL,如:https://api.example.com/v1/ 请求头 跨域时,引发发送多次请求 4. 路径,视网络上任何东西都是资源,均使用名词表示(可复数) https://api.example.com/v1/zoos https://api.example.com/v1/animals https://api.example.com/v1/employees 5.method GET :从服务器取出资源(一项或多项) POST :在服务器新建一个资源 PUT :在服务器更新资源(客户端提供改变后的完整资源) PATCH :在服务器更新资源(客户端提供改变的属性) DELETE :从服务器删除资源 6.过滤,通过在url上传参的形式传递搜索条件 https://api.example.com/v1/zoos?limit=10:指定返回记录的数量 https://api.example.com/v1/zoos?offset=10:指定返回记录的开始位置 7.状态码 8.错误处理,状态码是4xx时,应返回错误信息,error当做key { error: "Invalid API key" } 9.返回结果,针对不同操作,服务器向用户返回的结果应该符合以下规范。 GET /collection:返回资源对象的列表(数组) GET /collection/resource:返回单个资源对象 POST /collection:返回新生成的资源对象 PUT /collection/resource:返回完整的资源对象 PATCH /collection/resource:返回完整的资源对象 DELETE /collection/resource:返回一个空文档 10. Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。 {"link": { "rel": "collection https://www.example.com/zoos", "href": "https://api.example.com/zoos", "title": "List of zoos", "type": "application/vnd.yourformat+json" }}
csrf装饰器的使用
from django.views.decorators.csrf import csrf_exempt,csrf_protect # @csrf_protect #需要验证 # @csrf_exempt #不需要验证 # 给CBV加装饰器 from django.views import View from django.utils.decorators import method_decorator # 方式一:使用method_decorator class StudentsView(View): @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): return super(StudentsView, self).dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs): print('get方法') return HttpResponse('GET') def post(self, request, *args, **kwargs): return HttpResponse('POST') def put(self, request, *args, **kwargs): return HttpResponse('PUT') def delete(self, request, *args, **kwargs): return HttpResponse('DELETE') #方式二:给类加装饰器,name标明要装饰的函数 @method_decorator(csrf_exempt,name='dispatch') class ClassView(View): @method_decorator(csrf_exempt) def dispatch(self, request, *args, **kwargs): return super(ClassView, self).dispatch(request, *args, **kwargs) def get(self, request, *args, **kwargs): print('get方法') return HttpResponse('GET') def post(self, request, *args, **kwargs): return HttpResponse('POST') def put(self, request, *args, **kwargs): return HttpResponse('PUT') def delete(self, request, *args, **kwargs): return HttpResponse('DELETE')
基于Django Rest Framework框架实现
1. 认证和授权
a. 用户url传入的token认证
from django.conf.urls import url, include from web.viewsimport TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ]
from django.shortcuts import render,HttpResponse from django.views import View from rest_framework.views import APIView from rest_framework.request import Request from rest_framework.authentication import BasicAuthentication from rest_framework import exceptions from app01.models import * class MyAuthentication(object): """ 用户认证,如果验证成功后返回元组: (用户,用户Token) :param request: 加工后的request :return: (user,auth) 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异常 """ def authenticate(self,request): # request是加工过后对request,request._request是原来对request username = request._request.GET.get('username') password = request._request.GET.get('password') print(username,password) user = UserInfo.objects.filter(username=username,password=password).first() if not user: raise exceptions.AuthenticationFailed("验证失败") return (user,'auth') #self.user, self.auth = user_auth_tuple def authenticate_header(self,val): pass class StudentsView(APIView): authentication_classes = [MyAuthentication,] def get(self, request, *args, **kwargs): # self.dispatch(request, *args, **kwargs) print(request.authenticators,"对象列表") #[<app01.views.MyAuthentication object at 0x105ca9b38>] 对象列表 print(request._authenticator,"对象") #<app01.views.MyAuthentication object at 0x105ca9b38> 对象 print(request.user.username) #zhou print(request.auth) #auth return HttpResponse("ok") def post(self, request, *args, **kwargs): return HttpResponse('POST') def put(self, request, *args, **kwargs): return HttpResponse('PUT') def delete(self, request, *args, **kwargs): return HttpResponse('DELETE')
b. 登陆后生成token保存到数据库,访问时根据URL传入的token认证
from django.conf.urls import url from django.contrib import admin from app01 import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/v1/auth', views.Authview.as_view()), url(r'^api/v1/order', views.Orderview.as_view()), ]
from django.shortcuts import render,HttpResponse,render from rest_framework.views import APIView from rest_framework.request import Request from rest_framework import exceptions from rest_framework.authentication import BaseAuthentication from app01.models import * from django.http import JsonResponse def md5(user): """ 制作token """ import hashlib import time stime = str(time.time()) m = hashlib.md5(bytes(user,encoding="utf-8")) m.update(bytes(stime,encoding="utf-8")) return m.hexdigest() class Authview(APIView): """ 登陆校验,登陆成功后生成一个token存到数据库 """ def post(self,request): ret = {"code":1000,"msg":None} username = request._request.POST.get("username") pwd = request._request.POST.get("pwd") try: user = UserInfo.objects.filter(username=username,password=pwd).first() if not user: ret["code"] = 1001 ret["msg"] = "用户名或密码错误" else: token = md5(username) #有则更新,没有则创建 UserToken.objects.update_or_create(user=user,defaults={"token":token}) ret["token"] = token except Exception as e: ret['code'] = 1002 ret['msg'] = '请求出现错误:%s'%e return JsonResponse(ret) class Myauthenticat(BaseAuthentication): """ 自定义认证类,必须实现这两个方法 """ def authenticate(self,request): token = request._request.GET.get("token") if not token: raise exceptions.AuthenticationFailed("认证失败") obj = UserToken.objects.filter(token=token).first() if not obj: raise exceptions.AuthenticationFailed("验证码错误") return (obj.user,obj.token) def authenticate_header(self,val): pass class Orderview(APIView): # 认证 authentication_classes=[Myauthenticat,] def get(self,request): print(request.user,request.auth) # UserInfo object ,b5c552a6c744818c8fe10d7f8554a4c6 res = {"code":1000,"msg":None,"data":None} try: res["data"] = {"数据":123} except exceptions as e: res["msg"] = "请求出现错误:%s"%e res["code"] = 1002 return JsonResponse(res)
c. 认证类必须继承BaseAuthentication
1. 认证类,必须继承:from rest_framework.authentication import BaseAuthentication 2. 其他认证类:BasicAuthentication
d. 全局使用在配置文件中配置,单个CBV使用或者不用认证在类中重写 authentication_classes=[] 即可
REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES':['appo1.utils.auth.Myauthenticat'], # 'UNAUTHENTICATED_USER':lambda :'匿名用户', #定义是匿名用户时request.user的值 'UNAUTHENTICATED_USER':None, #定义是匿名用户时request.user的值 request.user = None 'UNAUTHENTICATED_TOKEN':None #定义是匿名用户时request.auth的值 request.auth = None }
e. 三种返回值
- 返回值: - None,我不管了,下一认证来执行。 - raise exceptions.AuthenticationFailed('用户认证失败') # from rest_framework import exceptions - (元素1,元素2) # 元素1赋值给request.user; 元素2赋值给request.auth
f. 源码流程
- 调用 dispatch - 封装request - 调用 initial - 调用 perform_authentication 认证 - 调用request.user - 获取定义的认证类(全局/局部),通过列表生成式创建对象列表。 - 循环调用对象的authenticate方法
-如果通过认证返回一个元组,把第一个元素赋值给request.user,
第二个元素赋值给request.auth
2. 权限认证
a. 权限认证,继承 BasePermission实现 has_permission方法
from rest_framework.permissions import BasePermission
from rest_framework.permissions import BasePermission class SVIPPermission(BasePermission): message = "必须是SVIP才可以访问" # 定义没权限时返回的信息 def has_permission(self, request, view): # 这里的request是封装过后的request,并且通过认证后request.user和request.auth已经有值 if request.user.user_type == 3: return True else: return False def has_object_permission(self, request, view, obj): pass #在需要的CBV中添加属性 permission_classes = [SVIPPermission,]
has_object_permission(self,request,view,obj): 对单个对象进行验证
访问详细页时走RetrieveModelMixin里的get_obj() 方法 最终调用到has_object_permission方法
# GenericAPIView中get_object时调用 def has_object_permission(self, request, view, obj): """ 视图继承GenericAPIView,并在其中使用get_object时获取对象时,触发单独对象权限验证 Return `True` if permission is granted, `False` otherwise. :param request: 封装后的requrst :param view: 视图函数对象 :param obj: model对象 :return: True有权限;False无权限 """ if request.user == "管理员": return True
b. 全局使用配置,不需权限认证的类中重写 permission_classes = [] 即可
REST_FRAMEWORK = { #认证 'DEFAULT_AUTHENTICATION_CLASSES':['app01.utils.auth.Myauthenticat'], # 'UNAUTHENTICATED_USER':lambda :'匿名用户', #定义是匿名用户时request.user的值 'UNAUTHENTICATED_USER':None, #定义是匿名用户时request.user的值 request.user = None 'UNAUTHENTICATED_TOKEN':None, #定义是匿名用户时request.auth的值 request.auth = None #权限 "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.SVIPPermission'], }
c. 返回值
- 返回True 通过验证
- 返回 False 不通过验证,message=‘xxx’ 可自定义错误信息
d. 源码流程
-调用dispatch -对原来对request进行封装,里面包含原来对request -调用inital进入验证 -调用check_permissions方法-
获取定义对权限类,全局或局部的,用列表生成式创建对象列表
-循环调用所有对定制权限类的has_permission方法 返回True通过,否则抛出异常
3. 访问 次数/频率 限制
a. 自定制的根据ip限制访问频率类
from rest_framework.throttling import BaseThrottle import time res = {} class VisitThrottle(BaseThrottle): """ 每分钟小于三次 """ def __init__(self): self.history = None def allow_request(self, request, view): current_time = time.time() ip = request._request.META.get("REMOTE_ADDR") if ip not in res: res[ip] = [current_time,] return True history=res.get(ip) self.history = history while history and history[-1] < current_time-60: history.pop() if len(history)<3: history.insert(0,current_time) return True # return True # 表示可以继续访问 # return False # 表示访问频率太高,被限制 def wait(self): """ 显示剩余事件 """ current_time = time.time() return 60 - (current_time - self.history[-1]) class Orderview(APIView): #频率限制 throttle_classes = [VisitThrottle,] def get(self, request): # self.dispatch() print(request.user, request.auth) # UserInfo object ,b5c552a6c744818c8fe10d7f8554a4c6 res = {"code": 1000, "msg": None, "data": None} try: res["data"] = {"数据": 123} except exceptions as e: res["msg"] = "请求出现错误:%s" % e res["code"] = 1002 return JsonResponse(res) def throttled(self, request, wait): """ 访问次数被限制时,定制错误信息 """ class Throttled(exceptions.Throttled): default_detail = '请求被限制.' extra_detail_singular = '请 {wait} 秒之后再重试.' extra_detail_plural = '请 {wait} 秒之后再重试.' raise Throttled(wait)
REST_FRAMEWORK = { #全局认证 'DEFAULT_AUTHENTICATION_CLASSES':['app01.utils.auth.Myauthenticat'], # 'UNAUTHENTICATED_USER':lambda :'匿名用户', #定义是匿名用户时request.user的值 'UNAUTHENTICATED_USER':None, #定义是匿名用户时request.user的值 request.user = None 'UNAUTHENTICATED_TOKEN':None, #定义是匿名用户时request.auth的值 request.auth = None #全局权限 "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.SVIPPermission'], #全局控制频率 'DEFAULT_THROTTLE_CLASSES':['app01.utils.throttle.VisitThrottle'], #内置类截流配置 'DEFAULT_THROTTLE_RATES':{ 'zhou':'4/m', #每分钟4次 } }
b. 内置控制频率类(推荐使用)
from rest_framework.throttling import SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): scope = 'zhou' # 在配置文件里设置的频率 def get_cache_key(self, request, view): """ :param request:封装后的request :param view: :return: 字典的键,用这个键作为限制的根据,例如限制一个ip的频率return self.get_ident(request) 限制一个账号的频率 return request.user.username """ return self.get_ident(request) # 得到IP class Orderview(APIView): # 频率控制 throttle_classes = [VisitThrottle, ] def get(self, request): # self.dispatch() print(request.user, request.auth) # UserInfo object ,b5c552a6c744818c8fe10d7f8554a4c6 res = {"code": 1000, "msg": None, "data": None} try: res["data"] = {"数据": 123} except exceptions as e: res["msg"] = "请求出现错误:%s" % e res["code"] = 1002 return JsonResponse(res) def throttled(self, request, wait): """ 访问次数被限制时,定制错误信息 """ class Throttled(exceptions.Throttled): default_detail = '请求被限制.' extra_detail_singular = '请 {wait} 秒之后再重试.' extra_detail_plural = '请 {wait} 秒之后再重试.' raise Throttled(wait)
REST_FRAMEWORK = { #全局认证 'DEFAULT_AUTHENTICATION_CLASSES':['app01.utils.auth.Myauthenticat'], # 'UNAUTHENTICATED_USER':lambda :'匿名用户', #定义是匿名用户时request.user的值 'UNAUTHENTICATED_USER':None, #定义是匿名用户时request.user的值 request.user = None 'UNAUTHENTICATED_TOKEN':None, #定义是匿名用户时request.auth的值 request.auth = None #全局权限 "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.SVIPPermission'], #全局控制频率 'DEFAULT_THROTTLE_CLASSES':['app01.utils.throttle.VisitThrottle'], #内置类截流配置 'DEFAULT_THROTTLE_RATES':{ 'zhou':'4/m', #每分钟4次 } }
c. 源码流程
-调用dispatch -对原来对request进行封装,里面包含原来对request -调用inital进入验证 -调用check_throttles方法
-获取定义的节流类,全局或局部的,用列表生成式创建对象列表
-在这个方法里循环调用所有对定制限制频率类的allow_request方法 返回True通过,否则抛出异常,调用wait方法返回具体的剩余时间
4. 版本
a. 基于url的get传参方式,例如?version=v1
自定义
url(r'^users/', views.UserView.as_view(),name='users_list'),
from django.urls import reverse from rest_framework.versioning import BaseVersioning class ParamVersion(BaseVersioning): """ 在URL的GET参数中获取版本信息 """ def determine_version(self, request, *args, **kwargs): #在URL获得版本version的值 query_params相当于_request.GET version = request.query_params.get("version") print(version,"version") return version #返回版本号,在request.version就可以取到 class UserView(APIView): versioning_class = ParamVersion # 注意不要用列表 def get(self,request,*args,**kwargs): # 获取版本 print(request.version) #v1 # 获取版本管理的类 print(request.versioning_scheme) #<api.views.ParamVersion object at 0x105cb2860> # 反向生成URL,url需要参数时记得给参数 reverse_url1 = request.versioning_scheme.reverse(viewname='users_list',request=request,kwargs={"version":"v2"}) reverse_url2 =reverse('users_list',kwargs={'version':"v1"}) print(reverse_url1) #http://127.0.0.1:8000/api/v2/users/ print(reverse_url2) #/api/v1/users/ return HttpResponse("用户列表")
内置的类QueryParameterVersioning
url(r'^users/', views.UserView.as_view(),name='users_list'),
REST_FRAMEWORK = { 'DEFAULT_VERSION': 'v1', # 默认版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本 'VERSION_PARAM': 'version' # URL中获取值的key }
from rest_framework.versioning import QueryParameterVersioning class UserView(APIView): versioning_class = QueryParameterVersioning # 注意不要用列表 def get(self,request,*args,**kwargs): # 获取版本 print(request.version) #v1 # 获取版本管理的类 print(request.versioning_scheme) #<rest_framework.versioning.QueryParameterVersioning object at 0x105ce92e8> # 反向生成URL,url需要参数时记得给参数 reverse_url1 = request.versioning_scheme.reverse(viewname='users_list',request=request) reverse_url2 =reverse('users_list') print(reverse_url1) #http://127.0.0.1:8000/api/users/?version=v1 print(reverse_url2) #/api/users/ return HttpResponse("用户列表")
b. 基于URL的正则方式 例如:api/v1/users/ (推荐使用)
使用内置类URLPathVersioning
url(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view(), name='users_list'), #允许的版本有v1和v2
from rest_framework.versioning import URLPathVersioning class UserView(APIView): versioning_class = URLPathVersioning # 注意不要用列表 def get(self,request,*args,**kwargs): # 获取版本 print(request.version) #v2 # 获取版本管理的类 print(request.versioning_scheme) # <rest_framework.versioning.URLPathVersioning object at 0x105cf07b8>> # 自动反向生成URL reverse_url1 = request.versioning_scheme.reverse(viewname='users_list',request=request) #手动生成url,需要传参数 reverse_url2 =reverse('users_list',kwargs={'version':'v1'}) print(reverse_url1) #http://127.0.0.1:8000/api/v2/users/ print(reverse_url2) #/api/v1/users/ return HttpResponse("用户列表")
如果想全局使用直接在配置文件内配置(推荐使用)
#版本,基于URL的正则方式(全局) "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
c. 其他方式
AcceptHeaderVersioning #基于 accept 请求头方式 NamespaceVersioning #基于django路由系统的namespace HostNameVersioning #基于主机名方法
5. 解析器
根据请求头 content-type 选择对应的解析器就请求体内容进行处理。
request.POST中有值的条件
1. 请求头要求: Content-Type: application/x-www-form-urlencoded PS: 如果请求头中的 Content-Type: application/x-www-form-urlencoded,request.POST中才有值(去request.body中解析数据)。 2. 数据格式要求: name=alex&age=18&gender=男
各种解析器:
仅处理请求头content-type为application/json的请求体 <JSONParser> #发送json数据时仅request.data有值 仅处理请求头content-type为application/x-www-form-urlencoded 的请求体<FormParser> #发送表单数据时仅request.POST 和request.data 都有值 仅处理请求头content-type为multipart/form-data(二进制数据)的请求体<MultiPartParser> #request.data中有数据,如果是文件request.FILES中有 仅上传文件<FileUploadParser> #上传文件时request.FILES取值
# application/x-www-form-urlencoded 或 multipart/form-data时,request.POST中才有值
#导入 from rest_framework.parsers import JSONParser,FormParser,MultiPartParser,FileUploadParser
#配置 parser_classes = [JSONParser,FormParser,MultiPartParser,FileUploadParser]
取值:
request.data request.POST request.FILES
全局使用配置
REST_FRAMEWORK = { 'DEFAULT_PARSER_CLASSES':[ 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', 'rest_framework.parsers.MultiPartParser', 'rest_framework.parsers.FileUploadParser' ] }
注意:个别特殊的值可以通过Django的request对象 request._request 来进行获取
6. 序列化
序列化用于对用户请求数据进行验证和数据进行序列化。
a. 基于serializers.Serializer自定义
from rest_framework import serializers class UserSerializer(serializers.Serializer): id = serializers.IntegerField() username=serializers.CharField() password = serializers.CharField() user_type = serializers.CharField(source='get_user_type_display') #显示choices的中文 group = serializers.CharField(source='group.title') #外健字段 # roles = serializers.CharField(source='roles.all') #结果是一个queryset roles = serializers.SerializerMethodField() #要先显示详细自定义,写一个 get_ 拼接的函数 def get_roles(self,row): """ :param row: 就是一个model类对象 :return: 返回什么显示什么 """ queryset = row.roles.all() ret = [] for obj in queryset: ret.append({"id":obj.pk,"title":obj.title}) return ret class UserView(APIView): def get(self, request, *args, **kwargs): obj = UserInfo.objects.all() ser = UserSerializer(instance=obj,many=True) # ser.data 就是已经处理完的可序列化的数据 return HttpResponse(json.dumps(ser.data)) def post(self,request, *args, **kwargs): return HttpResponse("用户列表post")
b. 基于serializers.SerializerMethodField 自动生成字段
自己写类:
class MyField(serializers.CharField): def to_representation(self, value): print(value,"value") # zhou value # yu value return value from rest_framework import serializers class UserSerializer(serializers.ModelSerializer): user_type = serializers.CharField(source='get_user_type_display') group = serializers.CharField(source='group.title') group_id = serializers.IntegerField(source='group.pk') roles = serializers.SerializerMethodField() name = MyField(source='username') class Meta: model=models.UserInfo # fields='__all__' #处理所有的字段,但一些关联字段只显示id,要想详细的数据自己写 fields=["id","name","user_type",'group',"group_id","roles"] def get_roles(self,row): """ :param row: 就是一个model类对象 :return: 返回什么显示什么 """ queryset = row.roles.all() ret = [] for obj in queryset: ret.append({"id":obj.pk,"title":obj.title}) return ret class UserView(APIView): def get(self, request, *args, **kwargs): obj = models.UserInfo.objects.all() ser = UserSerializer(many=True,instance=obj) # ser.data 就是已经处理完的可序列化的数据 return HttpResponse(json.dumps(ser.data,ensure_ascii=False)) def post(self,request, *args, **kwargs): return HttpResponse("用户列表post")
[ { "id": 1, "name": "zhou", "user_type": "SVIP", "group": "A组", "group_id": 1, "roles": [ { "title": "兔子", "id": 1 }, { "title": "萝莉", "id": 2 } ] }, { "id": 2, "name": "yu", "user_type": "普通用户", "group": "B组", "group_id": 2, "roles": [ { "title": "兔子", "id": 1 } ] } ]
使用depth 自动序列化链表:
from rest_framework import serializers class UserSerializer(serializers.ModelSerializer): user_type = serializers.CharField(source='get_user_type_display') class Meta: model=models.UserInfo # fields='__all__' #处理所有的字段 fields=["id","username","user_type",'group',"roles"] depth = 2 # 深度 官方建议0~10 class UserView(APIView): def get(self, request, *args, **kwargs): obj = models.UserInfo.objects.all() ser = UserSerializer(many=True,instance=obj) # ser.data 就是已经处理完的可序列化的数据 return HttpResponse(json.dumps(ser.data,ensure_ascii=False)) def post(self,request, *args, **kwargs): return HttpResponse("用户列表post")
[ { "id": 1, "username": "zhou", "user_type": "SVIP", "group": { "id": 1, "title": "A组" }, "roles": [ { "id": 1, "title": "兔子" }, { "id": 2, "title": "萝莉" } ] }, { "id": 2, "username": "yu", "user_type": "普通用户", "group": { "id": 2, "title": "B组" }, "roles": [ { "id": 1, "title": "兔子" } ] } ]
生成URl:
from django.conf.urls import url from api import views urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view(), name='users_list'), url(r'^(?P<version>[v1|v2]+)/group/(?P<group_id>d+)$', views.UserView.as_view(), name='group_detail'), ]
from rest_framework import serializers class UserSerializer(serializers.ModelSerializer): user_type = serializers.CharField(source='get_user_type_display') # 生成URl,view_name是别名,lookup_field是查询依据的字段,lookup_url_kwarg是url里参数的名字 group = serializers.HyperlinkedIdentityField(view_name='group_detail',lookup_field='group_id',lookup_url_kwarg='group_id') class Meta: model=models.UserInfo # fields='__all__' #处理所有的字段 fields=["id","username","user_type",'group',"roles"] depth = 2 # 深度 官方建议0~10 class UserView(APIView): def get(self, request, *args, **kwargs): obj = models.UserInfo.objects.all() ser = UserSerializer(many=True,instance=obj,context={"request":request}) #生成url时需要加上context={"request":request} # ser.data 就是已经处理完的可序列化的数据 return HttpResponse(json.dumps(ser.data,ensure_ascii=False)) def post(self,request, *args, **kwargs): return HttpResponse("用户列表post")
[ { "id": 1, "username": "zhou", "user_type": "SVIP", "group": "http://127.0.0.1:8000/api/v2/group/1", "roles": [ { "id": 1, "title": "兔子" }, { "id": 2, "title": "萝莉" } ] }, { "id": 2, "username": "yu", "user_type": "普通用户", "group": "http://127.0.0.1:8000/api/v2/group/2", "roles": [ { "id": 1, "title": "兔子" } ] } ]
请求数据校验:
class PasswordValidator(object): def __init__(self, base): self.base = str(base) def __call__(self, value): if not value.startswith(self.base): message = '标题必须以 %s 为开头。' % self.base raise serializers.ValidationError(message) def set_context(self, serializer_field): """ This hook is called by the serializer instance, prior to the validation call being made. """ # 执行验证之前调用,serializer_fields是当前字段对象 pass class UserSerializerPOST(serializers.ModelSerializer): username=serializers.CharField(max_length=6,error_messages={"max_length":"最大长度为2"}) password = serializers.CharField(error_messages={'required': '密码不能为空'}, validators=[PasswordValidator('666')]) class Meta: model = models.UserInfo fields='__all__' # fields=["username","password","roles"] def validate_username(self,value): print(value,"value") #value就是username的值 from rest_framework import exceptions if not value.startswith("zhou"): raise exceptions.ValidationError("必须以zhou开头") else: return value class UserView(APIView): def post(self,request, *args, **kwargs): ser = UserSerializerPOST(data=request.data) if ser.is_valid(): # 通过校验 print(ser.validated_data,"validated_data") return HttpResponse("ok") else: print(ser.errors,"errors") return HttpResponse(json.dumps(ser.errors))
7. 分页
a. 根据页码进行分页,看第n页,每页显示n条数据
from rest_framework.pagination import PageNumberPagination,LimitOffsetPagination,CursorPagination class MyPageNumberPagination(PageNumberPagination): page_size = 2 # 每页显示的条数 page_query_param = 'page' # url里的参数key page_size_query_param = "size" #可以指定每页显示多少条数据的参数 默认None max_page_size = 5 #每页显示数据的最大限制 class UserSerializer(ModelSerializer): class Meta: model=models.Role fields="__all__" class UserView(APIView): def get(self,request, *args, **kwargs): # 获取所有数据 obj = models.Role.objects.all() # 创建分页器对象 pg = MyPageNumberPagination() # 在数据库中获取分页的数据 pager_roles = pg.paginate_queryset(queryset=obj,request=request,view=self) # 对数据序列化 ser = UserSerializer(instance=pager_roles,many=True) return Response(ser.data)
b. 位置和个数分页,在n个位置,往后查看n条
from rest_framework.pagination import LimitOffsetPagination,CursorPagination class MyPageNumberPagination(LimitOffsetPagination): default_limit = 2 # 每页显示的条数 limit_query_param = 'limit' # 每页查看多少条数据 offset_query_param = 'offset' #从第多少个开始 max_limit = 5 # 每页显示数据的最大限制 class UserSerializer(ModelSerializer): class Meta: model=models.Role fields="__all__" class UserView(APIView): def get(self,request, *args, **kwargs): # 获取所有数据 obj = models.Role.objects.all() # 创建分页器对象 pg = MyPageNumberPagination() # 在数据库中获取分页的数据 pager_roles = pg.paginate_queryset(queryset=obj,request=request,view=self) # 对数据序列化 ser = UserSerializer(instance=pager_roles,many=True) return Response(ser.data)
c. 游标分页,加密分页,显示上一页和下一页的URL
from rest_framework.pagination import CursorPagination class MyPageNumberPagination(CursorPagination): cursor_query_param = 'cursor' # url里GET参数 page_size = 5 # 每页显示几个 ordering = 'id' # 按什么字段排序 class UserSerializer(ModelSerializer): class Meta: model=models.Role fields="__all__" class UserView(APIView): def get(self,request, *args, **kwargs): # 获取所有数据 obj = models.Role.objects.all() # 创建分页器对象 pg = MyPageNumberPagination() # 在数据库中获取分页的数据 pager_roles = pg.paginate_queryset(queryset=obj,request=request,view=self) # 对数据序列化 ser = UserSerializer(instance=pager_roles,many=True) # return Response(ser.data) return pg.get_paginated_response(ser.data) #使用这个方法显示上一页和下一页的url
d. 可以在配置文件设置全局配置
"PAGE_SIZE":2, #每页数据条数
8. 视图
a. GenericAPIView
from rest_framework.response import Response from rest_framework.serializers import ModelSerializer from rest_framework.pagination import CursorPagination class MyPageNumberPagination(CursorPagination): """ 分页 """ cursor_query_param = 'cursor' # url里GET参数 page_size = 5 # 每页显示几个 ordering = 'id' # 按什么字段排序 class UserSerializer(ModelSerializer): """ 序列化 """ class Meta: model=models.Role fields="__all__" from rest_framework.generics import GenericAPIView class UserView(GenericAPIView): queryset = models.Role.objects.all() serializer_class = UserSerializer pagination_class = MyPageNumberPagination def get(self,request, *args, **kwargs): # 获取数据 obj = self.get_queryset() # 相当于models.Role.objects.all() # 获取分页的数据 pager_roles = self.paginate_queryset(obj) # 对数据序列化 ser = self.get_serializer(instance=pager_roles,many=True) # return Response(ser.data) return self.get_paginated_response(ser.data) #使用这个方法显示上一页和下一页的url
b. GenericViewSet
from django.conf.urls import url from api import views urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view({"get":"list","post":"add"}), name='users_list'), url(r'^(?P<version>[v1|v2]+)/detail/(?P<pk>d+)/$', views.UserView.as_view({"get":"list","delete":"delete","put":"edit"}), name='xiangxi'), ]
from rest_framework.response import Response from rest_framework.serializers import ModelSerializer from rest_framework.pagination import CursorPagination,PageNumberPagination class MyPageNumberPagination(PageNumberPagination): """ 分页 """ page_size = 2 # 每页显示的条数 page_query_param = 'page' # url里的参数key page_size_query_param = "size" #可以指定每页显示多少条数据的参数 默认None max_page_size = 5 #每页显示数据的最大限制 class UserSerializer(ModelSerializer): """ 序列化 """ class Meta: model=models.Role fields="__all__" from rest_framework.generics import GenericAPIView from rest_framework.viewsets import GenericViewSet,ModelViewSet,ViewSetMixin from rest_framework.mixins import ListModelMixin,CreateModelMixin class UserView(GenericViewSet): queryset = models.Role.objects.all() serializer_class = UserSerializer pagination_class = MyPageNumberPagination def list(self,request, *args, **kwargs): if kwargs.get("pk"): return HttpResponse("详细") # 获取数据 obj = self.get_queryset() # 相当于models.Role.objects.all() # 获取分页的数据 pager_roles = self.paginate_queryset(obj) # 对数据序列化 ser = self.get_serializer(instance=pager_roles,many=True) return Response(ser.data) # return self.get_paginated_response(ser.data) #使用这个方法显示上一页和下一页的url def add(self,request, *args, **kwargs): ser = UserSerializer(data=request.data) if ser.is_valid(): # 通过校验 print(ser.validated_data,"validated_data") return HttpResponse("ok") else: print(ser.errors,"errors") return HttpResponse(json.dumps(ser.errors)) def delete(self, request, *args, **kwargs): print(kwargs["pk"],"pk") return HttpResponse("删除") def edit(self, request, *args, **kwargs): return HttpResponse("编辑")
c. ModelViewSet(自定义URL)
from django.conf.urls import url from api import views urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^(?P<version>[v1|v2]+)/users/$', views.UserView.as_view({"get":"list","post":"create"}), name='users_list'), url(r'^(?P<version>[v1|v2]+)/users/(?P<pk>d+)/$', views.UserView.as_view({'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'}), name='xiangxi'), ]
from rest_framework.response import Response from rest_framework.serializers import ModelSerializer from rest_framework.pagination import PageNumberPagination class MyPageNumberPagination(PageNumberPagination): """ 分页 """ page_size = 2 # 每页显示的条数 page_query_param = 'page' # url里的参数key page_size_query_param = "size" #可以指定每页显示多少条数据的参数 默认None max_page_size = 5 #每页显示数据的最大限制 class UserSerializer(ModelSerializer): """ 序列化 """ class Meta: model=models.Role fields="__all__" from rest_framework.viewsets import ModelViewSet class UserView(ModelViewSet): queryset = models.Role.objects.all() serializer_class = UserSerializer
d. ModelViewSet(rest framework路由)
from django.conf.urls import url,include from rest_framework import routers from . import views router = routers.DefaultRouter() router.register(r'users', views.UserView) # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/', include(router.urls)), ]
from rest_framework.response import Response from rest_framework.serializers import ModelSerializer from rest_framework.pagination import PageNumberPagination class MyPageNumberPagination(PageNumberPagination): """ 分页 """ page_size = 2 # 每页显示的条数 page_query_param = 'page' # url里的参数key page_size_query_param = "size" #可以指定每页显示多少条数据的参数 默认None max_page_size = 5 #每页显示数据的最大限制 class UserSerializer(ModelSerializer): """ 序列化 """ class Meta: model=models.Role fields="__all__" from rest_framework.viewsets import ModelViewSet class UserView(ModelViewSet): queryset = models.Role.objects.all() serializer_class = UserSerializer
视图继承关系
9. 路由
a. 自定义路由
from django.conf.urls import url, include from web.views import s11_render urlpatterns = [ url(r'^test/$', s11_render.TestView.as_view()), url(r'^test.(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view()), url(r'^test/(?P<pk>[^/.]+)/$', s11_render.TestView.as_view()), url(r'^test/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)$', s11_render.TestView.as_view()) ] urls.py
from rest_framework.views import APIView from rest_framework.response import Response from .. import models class TestView(APIView): def get(self, request, *args, **kwargs): print(kwargs) print(self.renderer_classes) return Response('...')
b.半自动路由
from django.conf.urls import url, include from web.views import s10_generic urlpatterns = [ url(r'^test/$', s10_generic.UserViewSet.as_view({'get': 'list', 'post': 'create'})), url(r'^test/(?P<pk>d+)/$', s10_generic.UserViewSet.as_view( {'get': 'retrieve', 'put': 'update', 'patch': 'partial_update', 'delete': 'destroy'})), ]
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.viewsets import ModelViewSet from rest_framework import serializers from .. import models class UserSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo fields = "__all__" class UserViewSet(ModelViewSet): queryset = models.UserInfo.objects.all() serializer_class = UserSerializer
c. 全自动路由
from django.conf.urls import url,include from rest_framework import routers from . import views router = routers.DefaultRouter() router.register(r'users', views.UserView) # Wire up our API using automatic URL routing. # Additionally, we include login URLs for the browsable API. urlpatterns = [ url(r'^(?P<version>[v1|v2]+)/', include(router.urls)), ]
from rest_framework.serializers import ModelSerializer class UserSerializer(ModelSerializer): """ 序列化 """ class Meta: model=models.Role fields="__all__" from rest_framework.viewsets import ModelViewSet class UserView(ModelViewSet): queryset = models.Role.objects.all() serializer_class = UserSerializer
10. 渲染器
from rest_framework.serializers import ModelSerializer class UserSerializer(ModelSerializer): """ 序列化 """ class Meta: model=models.Role fields="__all__" from rest_framework.renderers import JSONRenderer, BrowsableAPIRenderer # JSON和浏览器的渲染器 from rest_framework.viewsets import ModelViewSet class UserView(ModelViewSet): renderer_classes = [JSONRenderer,BrowsableAPIRenderer] queryset = models.Role.objects.all() serializer_class = UserSerializer
全局配置
REST_FRAMEWORK = { #全局认证 # 'DEFAULT_AUTHENTICATION_CLASSES':['app01.utils.auth.Myauthenticat'], # 'UNAUTHENTICATED_USER':lambda :'匿名用户', #定义是匿名用户时request.user的值 'UNAUTHENTICATED_USER':None, #定义是匿名用户时request.user的值 request.user = None 'UNAUTHENTICATED_TOKEN':None, #定义是匿名用户时request.auth的值 request.auth = None #全局权限 # "DEFAULT_PERMISSION_CLASSES":['app01.utils.permission.SVIPPermission'], #全局控制频率 # 'DEFAULT_THROTTLE_CLASSES':['app01.utils.throttle.VisitThrottle'], #内置类截流配置 'DEFAULT_THROTTLE_RATES':{ 'zhou':'4/m', #每分钟4次 }, #版本,基于get传参 'DEFAULT_VERSION': 'v1', # 默认版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本 'VERSION_PARAM': 'version', # URL中获取值的key #版本,基于URL的正则方式(全局) "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", #全局设置的解析器 'DEFAULT_PARSER_CLASSES':[ 'rest_framework.parsers.JSONParser', 'rest_framework.parsers.FormParser', 'rest_framework.parsers.MultiPartParser', 'rest_framework.parsers.FileUploadParser' ], # 分页 "PAGE_SIZE":2, #每页数据条数 # 渲染器 "DEFAULT_RENDERER_CLASSES": [ 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ] }
错误处理
'CourseView' should either include a `queryset` attribute, or override the `get_queryset()` method.
'CourseView' should either include a `serializer_class` attribute, or override the `get_serializer_class()` method.
原因:
如果我们试图类,继承了:GenericAPIView 或 GenericAPIView的派生类,只要执行 get_queryset 时,可能会报错(queryset = None)
如果我们试图类,继承了:GenericAPIView 或 GenericAPIView的派生类,只要执行 get_serializer 时,可能会报错(serializer_class = None)
有时加上渲染器 BrowsableAPIRenderer浏览器访问时会报错,postman和api则不会
分析原因:
BrowsableAPIRenderer内部调用了get_queryset
解决方法一:
加上属性queryset = ... serializer_class = ...
解决方法二:
#继承APIView自己写,如果需要as_view里的对应关系可继承ViewSetMixin #from rest_framework.viewsets import GenericViewSet,ViewSetMixin #from rest_framework.views import APIView url(r'^(?P<version>[v1|v2]+)/auth/$', views.AuthView.as_view()), class MyView(APIView): ... url(r'^(P<version[v1|v2]+)/course/$',views.CourseView.as_view({'get':'fun1'})), class CourseView(ViewSetMixin,APIView):