django rest framemark
一 内容回顾
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
django rest framemark的更多相关文章
- 异步任务队列Celery在Django中的使用
前段时间在Django Web平台开发中,碰到一些请求执行的任务时间较长(几分钟),为了加快用户的响应时间,因此决定采用异步任务的方式在后台执行这些任务.在同事的指引下接触了Celery这个异步任务队 ...
- 《Django By Example》第四章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:祝大家新年快乐,这次带来<D ...
- django server之间通过remote user 相互调用
首先,场景是这样的:存在两个django web应用,并且两个应用存在一定的联系.某些情况下彼此需要获取对方的数据. 但是我们的应用肯经都会有对应的鉴权机制.不会让人家随随便便就访问的对吧.好比上车要 ...
- Mysql事务探索及其在Django中的实践(二)
继上一篇<Mysql事务探索及其在Django中的实践(一)>交代完问题的背景和Mysql事务基础后,这一篇主要想介绍一下事务在Django中的使用以及实际应用给我们带来的效率提升. 首先 ...
- Mysql事务探索及其在Django中的实践(一)
前言 很早就有想开始写博客的想法,一方面是对自己近期所学知识的一些总结.沉淀,方便以后对过去的知识进行梳理.追溯,一方面也希望能通过博客来认识更多相同技术圈的朋友.所幸近期通过了博客园的申请,那么今天 ...
- 《Django By Example》第三章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:第三章滚烫出炉,大家请不要吐槽文中 ...
- 《Django By Example》第二章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:翻译完第一章后,发现翻译第二章的速 ...
- 《Django By Example》第一章 中文 翻译 (个人学习,渣翻)
书籍出处:https://www.packtpub.com/web-development/django-example 原作者:Antonio Melé (译者注:本人目前在杭州某家互联网公司工作, ...
- Django
一.Django 简介 Django 是一个由 Python 写成的开放源代码的 Web 应用框架.它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容为主的网站的,即是 CMS(内容管理系统) ...
随机推荐
- Python+Selenium学习--访问连接
场景 web UI测试里最简单也是最基本的事情就是访问1个链接了. 在python的webdrive中,访问url时应该使用get方法. 代码 #!/usr/bin/env python # -*- ...
- Django1.0和2.0中的rest_framework的序列化组件之超链接字段的处理
大家看到这个标题是不是有点懵逼,其实我就是想要一个这样的效果 比如我get一条书籍的数据,在一对多的字段中我们显示一个url,看起来是不是很绚! 下面我们就来实现这么一个东西 首先我们一对多字段中的一 ...
- css定位研究
css的定位是很重要的一个知识点,要学会网页布局,一定要先把定位弄清楚,今天抽空整理一下这方面的知识. 1.块级元素和行内元素(内联元素) 块级元素:display值为block的元素就是块级元素,比 ...
- Spring Cloud入门教程(二):客户端负载均衡(Ribbon)
对于大型应用系统负载均衡(LB:Load Balancing)是首要被解决一个问题.在微服务之前LB方案主要是集中式负载均衡方案,在服务消费者和服务提供者之间又一个独立的LB,LB通常是专门的硬件,如 ...
- linux命令学习之:vim
1. 关于Vim vim是我最喜欢的编辑器,也是linux下第二强大的编辑器. 虽然emacs是公认的世界第一,我认为使用emacs并没有使用vi进行编辑来得高效. 如果是初学vi,运行一下vimtu ...
- ThreadLocal ——android消息机制handler在非主线程创建not called Looper.prepare() 错误的原因
引用自:https://www.jianshu.com/p/a8fa72e708d3 引出: 使用Handler的时候,其必须要跟一个Looper绑定.在UI线程可直接初始化Handler来使用.但是 ...
- 运行msckf_vio
MSCKF_vio是一种基于多状态约束卡尔曼滤波器的双目视觉里程计.其中多状态约束是指将多帧图像的相机位姿加入卡尔曼状态向量中,在进行卡尔曼增益之前通过多帧图像之间的约束进行最小二乘优化来估计特征点 ...
- 【转】我为什么把think in java 读了10遍
我在想写这篇博文之前,就曾经对我媳妇(她是做web前端的)讲,我把think in java看了几次几次,媳妇那时就用很羡慕和莫名的眼神看着我说,你真有毅力,我当时就蒙了,我以为她会说,你现在基础一定 ...
- PAT 1046 划拳(15)(代码)
1046 划拳(15)(15 分) 划拳是古老中国酒文化的一个有趣的组成部分.酒桌上两人划拳的方法为:每人口中喊出一个数字,同时用手比划出一个数字.如果谁比划出的数字正好等于两人喊出的数字之和,谁就赢 ...
- Numpy array分割
1.纵向分割 >>> import numpy as np >>> A = np.arange(12).reshape((3, 4)) >>> p ...