一 内容回顾
1 开发者模式
普通开发方式:前后端放在一起开发
前后端分离:前后端只通过 JSON 来交流,组件化、工程化不需要依赖后端去实现
2 后端开发:为前端提供url接口,也就是API或者接口的开发。永远返回HttpResponse。
3 django的FBC和CBV
FBV也就是:function base view的缩写,也就是视图函数。方法如下:
- # def users(request):
- # user_list=['alex','oldboy','egon']
- # return HttpResponse(json.dumps(user_list))
CBV也就是:class base view的缩写,也就是类的视图。方法如下:
注:写的类需要继承View内置的类的方法。在url中使用:类名.as_view()
在views.py文件中:
View的导入:from django.views impost View
- # class StudentView(View):
- #
- # def get(self,request,*args,**kwargs):
- # return HttpResponse('GET')
- #
- # def post(self,request,*args,**kwargs):
- # return HttpResponse('POST')
- #
- # def delete(self,request,*args,**kwargs):
- # return HttpResponse('DELETE')
- #
- # def put(self,request,*args,**kwargs):
- # return HttpResponse('PUT')
在url.py文件中:
url(r'^student/', views.StudentView.as_view()),
CBV补充:CBV是基于反射实现根据请求方式不同,执行不同的方法。
原理:也就是执行的顺序是url -> view方法 -> dispatch方法(反射执行其他:GET/POST/DELETE/PUT)
流程如下:
- class StudentView(View):
- def dispatch(self, request, *args, **kwargs):
- ret=super(StudentView,self).dispatch(request, *args, **kwargs)
- 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("PUST")
-
- def delete(self,request,*args,**kwargs):
- return HttpResponse('DELETE')
继承:多个类的共同功能,为了避免重复写代码,如下:
- class MyBaseView:
- def dispatch(self,request,*args,**kwargs):
- print('before')
- ret=super(MyBaseView,self).dispatch(request,*args,**kwargs)
- print('after')
- return ret
-
- class StudentView(MyBaseView,View):
- def get(self,request,*args,**kwargs):
- print('get方法')
- return HttpResponse('GET')
-
- def put(self, request, *args, **kwargs):
- print('put方法')
- return HttpResponse('PUT')
-
- def post(self, request, *args, **kwargs):
- print('post方法')
- return HttpResponse('POST')
-
- def delete(self, request, *args, **kwargs):
- print('delete方法')
- return HttpResponse('DELETE')
4 列表生成式:
- class Foo:
- def __init__(self):
- pass
-
- def work(self):
- print('this is working')
-
-
- class Bar:
- def __init__(self):
- pass
-
- def ect(self):
- print('this is ecting')
-
-
- objs=[item() for item in [Foo,Bar]]
- # 就相当于如下
- # objs=[]
- # for i in [Foo,Bar]:
- # objs.append(i())
-
-
- objs[0].work()
- objs[1].ect()
5 面向对象的封装:
1将共同的方法和属性封装到同一个类下面。
- class File:
- 文件增删改查方法
-
- Class DB:
- 数据库的方法
2 将数据封装到数据中。实例如下:
- class File:
- def __init__(self,a1,a2):
- self.a1 = a1
- self.xxx = a2
- def get:...
- def delete:...
- def update:...
- def add:...
-
- obj1 = File(123,666)
- obj2 = File(456,999)
6 ps扩展:
- class Request:
- def __init__(self,obj):
- self.obj=obj
-
-
- @property
- def user(self):
- return self.obj.authticate()
-
- class Auth:
- def __init__(self,name,age):
- self.name=name
- self.age=age
-
- def authticate(self):
- return self.name,self.age
- class APIView:
- def dispatch(self):
- return self.f2()
-
- def f2(self):
- a=Auth('alex',33)
- ret=Request(a)
- return ret.user
-
- obj=APIView()
- print(obj.dispatch())
二 中间件和csrf_token
1 django中csrf_token是怎么实现的:csrf_token是在process_view里面验证的。
2 解决csrf_token的方法:
2.1:直接在settings文件中将下面这行代码给注释掉
'django.middleware.csrf.CsrfViewMiddleware',
2.2:通过csrf_exempt装饰器,将不需要csrf_token的试图函数上加上这个装饰器。process_view方法就会检查试图的cerf_token是否免除。
- from django.views.decorators.csrf import csrf_exempt
-
- @csrf_exempt #
- def users(request):
- user_list=['alex','oldboy','egon']
- return HttpResponse(json.dumps(user_list))
2.3:类的视图如果需要免除csrf_token的验证,就需要在class类使用装饰器method_decorator。不过必须要加上dispatch这个方法。加在单独的方法上面无效。
使用方法一:直接加在类里面的dispatch方法上面
- from django.views.decorators.csrf import csrf_exempt
- from django.utils.decorators import method_decorator
- # method_decorator 装饰器使用方法一:
- class StudentView(View):
-
- @method_decorator(csrf_exempt)
- def dispatch(self,request,*args,**kwargs):
- ret=super(StudentView,self).dispatch(request,*args,**kwargs)
- 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')
使用方法二:在class类上面加上这个装饰器,不过需要多创一个属性值,@method_decorator(csrf_token,name='dispatch')
- # method_decorator 装饰器使用方法二:
-
- from django.views.decorators.csrf import csrf_exempt
- from django.utils.decorators import method_decorator
- @method_decorator(csrf_exempt,name='dispatch')
- class StudentView(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')
2.4 :csrf_protect装饰器,如果在settings文件中将'django.middleware.csrf.CsrfViewMiddleware',这段代码注释掉了的话需要某个试图函数通过csrf_token的验证,就在这个视图函数上面加上这个装饰器。
- from django.views.decorators.csrf import csrf_exempt
- @csrf_protect # 该函数需认证
- def users(request):
- user_list = ['alex','oldboy']
- return HttpResponse(json.dumps((user_list)))
3 django的中间件:如下
- - process_request
- - process_view
- - process_response
- - process_exception
- - process_render_template
django的中间件做过什么:权限;用户认证。
process_view做过什么:检查视图是否被免除csrf_token的验证;去请求体的cookie中获取csrf_token
三 restful规范
什么是restful:
- REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
- REST从资源的角度类审视整个网络,它将分布在网络中某个节点的资源通过URL进行标识,客户端应用通过URL来获取资源的表征,获得这些表征致使这些应用转变状态
- REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”
- 所有的数据,不过是通过网络获取的还是操作(增删改查)的数据,都是资源,将一切数据视为资源是REST区别与其他架构风格的最本质属性
- 对于REST这种面向资源的架构风格,有人提出一种全新的结构理念,即:面向资源架构(ROA:Resource Oriented Architecture)
restful是为了根据method的不同做这不同的操作。restful就是一个规范,只为了让我们更好的记住url。
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:指定返回记录的开始位置
- https://api.example.com/v1/zoos?page=2&per_page=100:指定第几页,以及每页的记录数
- https://api.example.com/v1/zoos?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序
- https://api.example.com/v1/zoos?animal_type_id=1:指定筛选条件
7 状态码
- 200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
- 201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
- 202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
- 204 NO CONTENT - [DELETE]:用户删除数据成功。
- 400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
- 401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
- 403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
- 404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
- 406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
- 410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
- 422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
- 500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。
http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html
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"
- }}
借鉴:http://www.ruanyifeng.com/blog/2014/05/restful_api.html
四 restful的API面试解答
问题:请说一下你对restful的认识。
解答:
- restful的API设计模式有API与用户的通信协议是基于HTTPs协议实现的,通过url传参的形式传递搜索条件的,还有错误处理等等。
- 在我没有遇到restful API之前需要记住各种各样的很多的url路径,而且写的代码全是靠着试图函数给实现的,代码看起来不是很高端,并且等等
- RESTful架构,是比较流行的一种互联网软件架构。REST,即Representational State Transfer的缩写。
- 说白点就是网站即软件,再白点就是一个服务软件支持http的四种方法:
- GET用来获取资源,POST用来新建资源、更新资源,PUT用来更新资源,DELETE用来删除资源。
-
- 在我遇到restful之后,不需要记住那么多的url,可以将共同的方法封装到一个类下面,通过调用在这个类的方
- 法来访问url,可以通过这个类调用多个方法。并且API很好的解决了跨域的问题。版本有更新的时候,还可以
- 将版本号写在url中来提示使用的版本。还可以自己写访问错误时返回的错误信息。传入参数时还可以通过url来传递参数等等,
-
-
借鉴 :
- RESTful架构,目前是比较流行的一种互联网软件架构。REST,即Representational State Transfer的缩写。
-
- 说白点就是网站即软件,再白点就是一个服务软件支持http的四种方法:
-
- GET用来获取资源,POST用来新建资源、更新资源,PUT用来更新资源,DELETE用来删除资源。
-
- 并对外提供一个或多个URI,每个URI对应一个资源;客户端通过URI配合上面的方法就可以和服务
-
- 段的软件交互。客户端主要是浏览器,使用restful框架的软件对http的支持也为了web应用带来方便。
-
- REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。他的贡献很多,
-
- 可以了解一下。本人工作的方向是SDN,也算是比较潮的东东,其中floodlight就用到了restful框架。
-
- 开发者为软件开发出一些功能,并提供URI api,用户就可以利用浏览器、curl等工具通过提供的URI
-
- 从软件中获得想要的信息或者设置软件的功能。
-
- 对于发开者来说,就是提供URI和URI对应的资源,并将他们对应上,类似dicts={'/path?':resource}。
-
- 比如重写http GET方法:首先获得客户端请求的url,解析url然后判断其对应的URI,由于URI与应一个资源,
-
- 那么url就可以访问这个资源了。具体实现上资源也就是方法或者一个类,要看具体实现了。
-
-
五 认证
1:有些API需要用户登陆过后,才能访问,有些不需要登陆就能够访问
2:基本使用认证组件:解决,创建两张表,用户登陆后,获取token,存入到数据库。
3:认证流程原理:
4:局部视图使用和全局使用,匿名用户request.user=None
5:内置认证类:
认证类,必须继承:from rest_framework.authentication import BaseAuthentication
其他认证类:BasicAuthentication
from rest_framework import exceptions from api import models from rest_framework.authentication import BaseAuthentication class FirstAuthtication(BaseAuthentication): def authenticate(self,request): pass def authenticate_header(self, request): pass class Authtication(BaseAuthentication): def authenticate(self,request): token = request._request.GET.get('token') token_obj = models.UserToken.objects.filter(token=token).first() if not token_obj: raise exceptions.AuthenticationFailed('用户认证失败') # 在rest framework内部会将整个两个字段赋值给request,以供后续操作使用 return (token_obj.user, token_obj) def authenticate_header(self, request): return 'Basic realm="api"'
使用:
创建类:继承BaseAuthentication; 实现:authenticate方法
返回值:None,我不管了,下一认证来执行;raise exceptions.AuthenticationFailed('用户认证失败') # from rest_framework import exceptions ; (元素1,元素2) # 元素1赋值给request.user; 元素2赋值给request.auth
局部使用:在view试图类中直接调用
from rest_framework.authentication import BaseAuthentication,BasicAuthentication class UserInfoView(APIView): """ 订单相关业务 """ authentication_classes = [BasicAuthentication,] def get(self,request,*args,**kwargs): print(request.user) return HttpResponse('用户信息')
全局使用:就是在settings文件中加上以下操作:
REST_FRAMEWORK = { # 全局使用的认证类 "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ], # "UNAUTHENTICATED_USER":lambda :"匿名用户" "UNAUTHENTICATED_USER":None, # 匿名,request.user = None "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None }
源码流程:
- - dispatch
- - 封装request
- - 获取定义的认证类(全局/局部),通过列表生成时创建对象。
- - initial
- - perform_authentication
- request.user(内部循环....)
六 权限
不用视图,不用权限访问
基本使用:
class MyPermission(object): def has_permission(self,request,view): if request.user.user_type != 3: return False return True class OrderView(APIView): """ 订单相关业务(只有SVIP用户有权限) """ permission_classes = [MyPermission,] def get(self,request,*args,**kwargs): # request.user # request.auth self.dispatch ret = {'code':1000,'msg':None,'data':None} try: ret['data'] = ORDER_DICT except Exception as e: pass return JsonResponse(ret)
源码流程:
另一种使用:类,必须继承:BasePermission,必须实现:has_permission方法
from rest_framework.permissions import BasePermission class SVIPPermission(BasePermission): message = "必须是SVIP才能访问" def has_permission(self,request,view): if request.user.user_type != 3: return False return True - 返回值: - True, 有权访问 - False,无权访问 - 局部 :在view视图文件 class UserInfoView(APIView): """ 订单相关业务(普通用户、VIP) """ permission_classes = [MyPermission1, ] def get(self,request,*args,**kwargs): return HttpResponse('用户信息') - 全局 :settings文件中 REST_FRAMEWORK = { "DEFAULT_PERMISSION_CLASSES:['api.utils.permission.SVIPPermission'] }
源码流程:
七 节流(控制访问频率)
控制访问频率:
创建访问次数: 类, 继承:BaseThrottle,实现:allow_request、wait
类, 继承:SimpleRateThrottle,实现:get_cache_key、scope = "Luffy"(配置文件中的key)
import time VISIT_RECORD = {} class VisitThrottle(object): """60s内只能访问3次""" def __init__(self): self.history = None def allow_request(self,request,view): # 1. 获取用户IP remote_addr = request.META.get('REMOTE_ADDR') ctime = time.time() if remote_addr not in VISIT_RECORD: VISIT_RECORD[remote_addr] = [ctime,] return True history = VISIT_RECORD.get(remote_addr) self.history = history while history and history[-1] < ctime - 60: history.pop() if len(history) < 3: history.insert(0,ctime) return True # return True # 表示可以继续访问 # return False # 表示访问频率太高,被限制 def wait(self): """ 还需要等多少秒才能访问 :return: """ ctime = time.time() return 60 - (ctime - self.history[-1])
局部使用:在view视图中
class AuthView(APIView): """ 用于用户登录认证 """ authentication_classes = [] permission_classes = [] throttle_classes = [VisitThrottle,] def post(self,request,*args,**kwargs): ret = {'code':1000,'msg':None} try: user = request._request.POST.get('username') pwd = request._request.POST.get('password') obj = models.UserInfo.objects.filter(username=user,password=pwd).first() if not obj: ret['code'] = 1001 ret['msg'] = "用户名或密码错误" # 为登录用户创建token token = md5(user) # 存在就更新,不存在就创建 models.UserToken.objects.update_or_create(user=obj,defaults={'token':token}) ret['token'] = token except Exception as e: ret['code'] = 1002 ret['msg'] = '请求异常' return JsonResponse(ret)
源码流程:
内置控制频率类:
from rest_framework.throttling import BaseThrottle,SimpleRateThrottle class VisitThrottle(SimpleRateThrottle): scope = "Luffy" def get_cache_key(self, request, view): return self.get_ident(request) class UserThrottle(SimpleRateThrottle): scope = "LuffyUser" def get_cache_key(self, request, view): return request.user.username
全局使用:在settings文件中
REST_FRAMEWORK = { # 全局使用的认证类 "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication','api.utils.auth.Authtication', ], # "DEFAULT_AUTHENTICATION_CLASSES":['api.utils.auth.FirstAuthtication', ], # "UNAUTHENTICATED_USER":lambda :"匿名用户" "UNAUTHENTICATED_USER":None, # 匿名,request.user = None "UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None "DEFAULT_PERMISSION_CLASSES":['api.utils.permission.SVIPPermission'], "DEFAULT_THROTTLE_CLASSES":["api.utils.throttle.UserThrottle"], "DEFAULT_THROTTLE_RATES":{ "Luffy":'3/m', "LuffyUser":'10/m', } }
八 版本
url通过get传参:
自定义:
http://127.0.0.1:8000/api/users/?version=v2 class ParamVersion(object): def determine_version(self, request, *args, **kwargs): version = request.query_params.get('version') return version class UsersView(APIView): versioning_class = ParamVersion def get(self,request,*args,**kwargs): #version = request._request.GET.get('version') #print(version) # version = request.query_params.get('version') # print(version) print(request.version) return HttpResponse('用户列表')
在URL中传参(推荐使用):在urls文件中
urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view()), ]
在settings文件中:
REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", "DEFAULT_VERSION":'v1', "ALLOWED_VERSIONS":['v1','v2'], "VERSION_PARAM":'version', }
在views文件中:
class UsersView(APIView): def get(self,request,*args,**kwargs): print(request.version) return HttpResponse('用户列表')
总结:
使用: 配置文件: REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", "DEFAULT_VERSION":'v1', "ALLOWED_VERSIONS":['v1','v2'], "VERSION_PARAM":'version', } 路由系统: urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^api/', include('api.urls')), ] urlpatterns = [ # url(r'^admin/', admin.site.urls), url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view(),name='uuu'), ] 视图: class UsersView(APIView): def get(self,request,*args,**kwargs): # 1. 获取版本 print(request.version) # 2. 获取处理版本的对象 print(request.versioning_scheme) # 3. 反向生成URL(rest framework) u1 = request.versioning_scheme.reverse(viewname='uuu',request=request) print(u1) # 4. 反向生成URL u2 = reverse(viewname='uuu',kwargs={'version':2}) print(u2) return HttpResponse('用户列表')
九 解析器
前戏:django:request.POST/ request.body
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=男 如: a. form表单提交 <form method...> input... </form> b. ajax提交 $.ajax({ url:... type:POST, data:{name:alex,age=18} # 内部转化 name=alex&age=18&gender=男 }) 情况一: $.ajax({ url:... type:POST, headers:{'Content-Type':"application/json"} data:{name:alex,age=18} # 内部转化 name=alex&age=18&gender=男 }) # body有值;POST无 情况二: $.ajax({ url:... type:POST, headers:{'Content-Type':"application/json"} data:JSON.stringfy({name:alex,age=18}) # {name:alex,age:18...} }) # body有值;POST无 # json.loads(request.body) rest_framework 解析器,对请求体数据进行解析
总结:
使用: 配置: REST_FRAMEWORK = { "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning", "DEFAULT_VERSION":'v1', "ALLOWED_VERSIONS":['v1','v2'], "VERSION_PARAM":'version', "DEFAULT_PARSER_CLASSES":['rest_framework.parsers.JSONParser','rest_framework.parsers.FormParser'] } 使用: class ParserView(APIView): # parser_classes = [JSONParser,FormParser,] """ JSONParser:表示只能解析content-type:application/json头 JSONParser:表示只能解析content-type:application/x-www-form-urlencoded头 """ def post(self,request,*args,**kwargs): """ 允许用户发送JSON格式数据 a. content-type: application/json b. {'name':'alex',age:18} :param request: :param args: :param kwargs: :return: """ """ 1. 获取用户请求 2. 获取用户请求体 3. 根据用户请求头 和 parser_classes = [JSONParser,FormParser,] 中支持的请求头进行比较 4. JSONParser对象去请求体 5. request.data """ print(request.data) return HttpResponse('ParserView') 源码流程 & 本质: a. 本质 请求头 :... 状态码: ... 请求方法:... b. 源码流程 - dispatch: request封装 - request.data
十 序列化
部分总结:
1. 写类
class RolesSerializer(serializers.Serializer): id = serializers.IntegerField() title = serializers.CharField() class UserInfoSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo # fields = "__all__" fields = ['id','username','password',]
2. 字段
a. title = serializers.CharField(source="xxx.xxx.xx.xx") b. title = serializers.SerializerMethodField() class UserInfoSerializer(serializers.ModelSerializer): rls = serializers.SerializerMethodField() # 自定义显示 class Meta: model = models.UserInfo fields = ['id','username','password','rls',] # 自定义方法 def get_rls(self, row): role_obj_list = row.roles.all() ret = [] for item in role_obj_list: ret.append({'id':item.id,'title':item.title}) return ret c. 自定义类
3 自动序列化连表
class UserInfoSerializer(serializers.ModelSerializer): class Meta: model = models.UserInfo # fields = "__all__" fields = ['id','username','password','group','roles'] depth = 1 # 0 ~ 10
4. 生成链接
class UserInfoSerializer(serializers.ModelSerializer): group = serializers.HyperlinkedIdentityField(view_name='gp',lookup_field='group_id',lookup_url_kwarg='xxx') class Meta: model = models.UserInfo # fields = "__all__" fields = ['id','username','password','group','roles'] depth = 0 # 0 ~ 10 class UserInfoView(APIView): def get(self,request,*args,**kwargs): users = models.UserInfo.objects.all() ser = UserInfoSerializer(instance=users,many=True,context={'request': request}) ret = json.dumps(ser.data, ensure_ascii=False) return HttpResponse(ret)
源码:
对象, Serializer类处理; QuerySet,ListSerializer类处理; # ser.data
十一 数据请求校验
class XXValidator(object): def __init__(self, base): self.base = 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 UserGroupSerializer(serializers.Serializer): title = serializers.CharField(error_messages={'required':'标题不能为空'},validators=[XXValidator('老男人'),]) class UserGroupView(APIView): def post(self,request,*args,**kwargs): ser = UserGroupSerializer(data=request.data) if ser.is_valid(): print(ser.validated_data['title']) else: print(ser.errors) return HttpResponse('提交数据')
注意问题: 自定义验证规则时,需要钩子函数?请问钩子函数如何写?
十二 分页
分页在这里分为了三部分:普通分页,基于limit offset分页和基于加密的分页
Response:直接就可以序列化,不需要json。
普通分页:查看第几页,显示几条数据,但是如果数据量大的话,分页约往后面越慢
from rest_framework.pagination import PageNumberPagination class MyPageNumberPagination(PageNumberPagination): page_size = 2 page_size_query_param = 'size' max_page_size = 5 page_query_param = 'page' class Pager1View(APIView): def get(self,request,*args,**kwargs): # 获取所有数据 roles = models.Role.objects.all() # 创建分页对象 # pg = MyPageNumberPagination() pg = PageNumberPagination() # 在数据库中获取分页的数据 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)
基于limit offset的分页:在n个位置,向后面查看n条数据。LimitOffsetPagintion:基于limit offset分页的原生类
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 = 2 limit_query_param = 'limit' offset_query_param = 'offset' max_limit = 5 class Pager1View(APIView): def get(self,request,*args,**kwargs): # 获取所有数据 roles = models.Role.objects.all() # 创建分页对象 # 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)
基于加密的分页:记住每一页的最大值和最小值的ID,如果点击下一页就从最大值的ID向后数n条数据;如果点击的是上一页,就从最小值向前数n条数据。cursorParination:加密分页,不需要传入任何的参数。
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' page_size_query_param = None max_page_size = None class Pager1View(APIView): def get(self,request,*args,**kwargs): # 获取所有数据 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)
Pagination下面PageNumberpagination:分页器的一个模块,在settings文件中配置一下PAGE_SIZE:每页数据的条数。
REST_FRAMEWORK = { "PAGE_SIZE":2, }
十二 视图
过去:
class Pager1View(View): pass
现在:
class Pager1View(APIView): # View pass
无用:
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)
GenericViewSet(ViewSetMixin, generics.GenericAPIView):
路由:
url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list'})),
视图:
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)
增删改查:
路由:as_view():括号里面是可以传入参数的。
url(r'^(?P<version>[v1|v2]+)/v1/$', 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'})),
视图:
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
总结:
a. 增删改查 ModelViewSet
b. 增删 CreateModelMixin,DestroyModelMixin GenericViewSet
c. 复杂逻辑 GenericViewSet 或 APIView
PS: 还债:
GenericAPIView.get_object
check_object_permissions
has_object_permission
十三 路由
在url后面加上format=json:写给解析器看的,也等于早url里面加上一个命名分组,format一样的
自动生成url:urls文件导入routers模块;然后再router=routers.DefaultRouter();最后在router.register(r'...',views.类名);最最后url(r'^',incloude(router.urls))。以上四步是是自动生成增删改查的url的步骤。
a. url(r'^(?P<version>[v1|v2]+)/parser/$', views.ParserView.as_view()), b. url(r'^(?P<version>[v1|v2]+)/v1/$', views.View1View.as_view({'get': 'list','post':'create'})), c. # 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. 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 = [ url(r'^(?P<version>[v1|v2]+)/', include(router.urls)), ]
十四 渲染器
JSONRenderer:json渲染
在views文件中:局部渲染renderer_classes = [JSONRenderer,BrowsableAPIRenderer]
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer class TestView(APIView): renderer_classes = [JSONRenderer,BrowsableAPIRenderer] def get(self, request, *args, **kwargs): # 获取所有数据 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)
在settings文件中全局渲染:
views文件:
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer class TestView(APIView): def get(self, request, *args, **kwargs): # 获取所有数据 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)
settings文件中
REST_FRAMEWORK = { "DEFAULT_RENDERER_CLASSES":[ 'rest_framework.renderers.JSONRenderer', 'rest_framework.renderers.BrowsableAPIRenderer', ] }
找模板的顺序:先去最外层查找,如果没有再去某个app里面寻找
十五 面试题
我的写的类的试图最多继承了几个类
6个类中有四个是增删改查的类,有一个是局部更新的类,还有一个类是专门继承其他的类的。
你做过分页吗,数据量有多大:django自带的分页功能不完善,我自己定义过分页,数据量差不多2000左右。数据量越大,分页向后查找的速度就会越慢。在数据量非常大的情况下,数据没有必要全部的显示,显示一定的数量就可以,避免数据量多的分页。
rest_framowork的请求生命周期:
1.请求到达服务端,经过WSGI和中间件到达路由系统 2.路由系统执行配置的CBV或者FBV中的dispatch方法 3.在dispatch方法中,request方法被封装添加了解析器,认证方法及选择器等方法 4.然后执行initial方法 5.再获取版本,进行认证操作,权限操作和节流操作 6.最后执行自定义的get,post,push,delete等自定义方法 7.在执行initial方法之前,通过try来捕获可能出现的异常 8.如果出现异常,就执行handle_exception方法来处理捕获到的异常 9.不管是否出现异常,最后的返回值都通过finalize_response方法来处理响应的内容
什么请况下request.POST没有值:
- 只有请求header中的'Content-Type'是'application/x-www-form-urlencoded'request.POST'的情况下POST里面才会有值,,其它情况下就会没有值
序列化检验分为哪些步骤:
自己写的类继承:serializer
字段继承:SerializerMethodField。
生成url