Django之REST framework源码分析
前言:
Django REST framework,是1个基于Django搭建 REST风格API的框架;
1、什么是API呢?
API就是访问即可获取数据的url地址,下面是一个最简单的 Django API,访问http://127.0.0.1:8000/,返回用户列表;
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^user/', views.user),
]
路由
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
users=['大波','张开','人间' ]
def user(request):
return JsonResponse(users,safe=False)
视图
2、什么是restfunAPI呢?
如果新增增加用户功能,再向用户暴露1个API接口 http://127.0.0.1:8000/useradd/?new_user='A'
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^user/', views.user),
url(r'^useradd/', views.user_add), #增加用户功能
]
路由
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
users=['大波','张开','人间' ] def user(request):
return JsonResponse(users,safe=False) def user_add(request): #增加用户功能
new_user=request.GET.get('new_user')
users.append(new_user)
return JsonResponse(users,safe=False)
视图
在增加用户功能上再添加删除、编辑用户功能 向用户暴露 4个api接口
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^user/', views.user), #显示用户列表
url(r'^useradd/$', views.user_add), #增加用户功能
url(r'^userdelete/\d+/$', views.user_delete), # 删除用户功能
url(r'^useredit/\d+/$', views.user_edit), #编辑用户信息
]
路由
如果按照这种开发模式,随着项目功能的完善,弊端也会暴露出来,维护的API接口也会越来越多,在协作开发中出现种种问题;
于是一种API编写规范出现了,他就是RESTful规范;
在1个视图中通过判断用户的请求method不同(get/post/put/delete),后台做不同的操作;这样就可以只暴露1个API给用户并且维护了代码的整洁,这就是restful 规范之一;
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^user/', views.user), #显示用户列表
url(r'^user/(?P<pk>\d+)/$',views.user), #编辑和删除需要一个PK 在URL中 ]
路由
from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
users=['大波','张开','人间' ] def user(request,*args,**kwargs):
if request.method=='GET':
#如果请求方法为 GET 获取数据列表
return JsonResponse(users,safe=False) elif request.method=='POST':
# 如果请求方法为 POST 添加列表数据
return JsonResponse(users, safe=False) elif request.method=='PUT':
# 如果请求方法为 PUT,更新操作
pk=kwargs.get('pk')
return JsonResponse(users, safe=False) elif request.method=='DELETE':
pk=kwargs.get('pk')
# 如果请求方法为 DELETE,删除操作
return JsonResponse(users, safe=False)
视图
总结:
restful 风格API有很多规范,接下来我们介绍一下它都有哪些规范?
3、REST framework 请求的生命周期
一、RESTful API设计规范
RESTful API的设计需要遵循很多规范,但是只是建议,如果不遵守这种规范也是可以的,以下是 它部分规范介绍;
1、面向资源
面向资源指得是把网络中可以访问到的任何url都想象成1个资源,这个资源可以是1个文件、 1张图片、1个视频....。
例如:
http://www.le.com/videos/
http://www.le.com/files/
http://www.le.com/pictures/
注意 1级域名后边(videos、files、pictures)都是名词
2、版本号
我们的API不是一成不变的,如果版本迭代也需要体现在url上,以供用户选择不同版本;
例如:
http://www.le.com/v1/files/ 访问版本1API资源
http://www.le.com/v2/files/ 访问版本2API资源
3、返回值
API在放回数据的同时也返回请求的状态码;
例如:
return HttpRespose(josn.dumps(ret),status=200) 访问成功
return HttpRespose(josn.dumps(ret),status=3XX) 权限错误
return HttpRespose(josn.dumps(ret),status=4XX) 资源存在
return HttpRespose(josn.dumps(ret),status=5XX) 服务器错误
详细:
OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
NO CONTENT - [DELETE]:用户删除数据成功。
INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。 更多看这里:http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 常用状态码列表
状态码列表
4、API与用户的通信协议,总是使用https协议
5、域名规范
例如:
前端Vue使用:https://www.le.com
后端Django:https://api.le.com 注意会出现跨域问题
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、返回结果: 根据用户不同的操作,服务器放过的结果应该符合以下规范
GET /collection:返回资源对象的列表(数组)
GET /collection/resource:返回单个资源对象
POST /collection:返回新生成的资源对象
PUT /collection/resource:返回完整的资源对象
PATCH /collection/resource:返回完整的资源对象
DELETE /collection/resource:返回一个空文档
8、Hypermedia API,RESTful API最好做到Hypermedia,即返回结果中提供链接,连向其他API方法,使得用户不查文档,也知道下一步应该做什么。
[
{
'name':'alex',
'age':'',
'user_type': 'http://www.le.com/api/v1/usergroups/1/'
},
{
'name':'alex',
'age':'',
'user_type': 'DS'
},
{
'name':'alex1',
'age':'',
'user_type': 'DS'
},
{
'name':'alex2',
'age':'',
'user_type': 'DS'
} ]
9.总结
restful 规范本质就是1个约束 web 接口,增、删、改、查方法的规范,方便调用方维护、记忆url;
三. 基于DjangoRest Framework框架实现 restful API
如果自己去实现1个restful api需要遵循以上很多restful API规则,实现起来比较困难,
于是Django的 Rest framework框架出现了,通过它就可以无需关注众多restful规范, 快速实现restful API;
、安装 djangorestframwork
pip install djangorestframework -i https://pypi.douban.com/simple/ 、安装 virtual env(虚拟环境,相当于做了环境隔离)
pip install virtualenv 、创建1个存放虚拟环境的目录
mkdir virtualenv
cd virtualenv 、创建1个干净的虚拟环境(--no-site-packaes) virtualenv --no-site-package
Using base prefix 'd:\\python3.6.1'
New python executable in D:\virtualenv\ven
Installing setuptools, pip, wheel...done. 、激活虚拟环境
linux
source venv/bin/activate DOS
cdvirtualenv\venv1\Scripts
activate 安装Django
>>> exit()
(venv1) D:\virtualenv\venv1\Scripts>pip install django 、退出虚拟环境 deactivate
前戏 Virtual env
Django framework源码分析
想要深入了解 Rest framework框架是如何实现restful API的就要分析它的源码;
Django framework源码入口
由于所有CBV首先要执行dispatch方法,所有它就是Django framework源码的入口
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView #导入 APIView #类继承了APIView就是 restfulAPI了,但是APIView继承了Django的view本质是对Django view的一种二次封装;
class UsersView(APIView):
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
#1.二次封装request(原request,parsers=解析用户请求的数据,authenticators=认证相关, negotiator=选择相关, parser_context='封装的self和参数')
request = self.initialize_request(request, *args, **kwargs)
self.request = request
#2、request被更换为封装完毕的request
self.headers = self.default_response_headers # deprecate?
#3、执行initial 获取版本信息、 认证、权限、节流
try:
self.initial(request, *args, **kwargs) # Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed response = handler(request, *args, **kwargs) except Exception as exc:
response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
def get(self,request,*args,**kwargs):
self.dispatch() # 执行APIView的 dispatch 也是 framwork的源码入口
return HttpResponse('...')
#
Django framework分析结果
经过对Django framework分析得知 Django restframework的生命周期
1、中间件
2、路由系统
3、CBV/FBV(视图)
4、执行dispath方法
二次封装request(原request,解析用户请求的数据,authenticators=认证相关, negotiator=选择相关, parser_context='封装的self和参数') try
获取版本信息
认证
权限
节流
respose=执行 GET/POST/PUT/DELETE方法 except
respose = 异常 finally
处理响应内容
1、版本号设置
我们的API不是一成不变的,如果版本迭代就需要以版本号加以区别,让用户选择不同的版本;
1.1 版本控制源码分析
1.2 配置使用
视图配置
方式1: http://127.0.0.1:8000/users/?version=v1
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView #导入 APIView
from rest_framework.versioning import QueryParameterVersioning
from rest_framework.versioning import URLPathVersioning class UsersView(APIView):
versioning_class=QueryParameterVersioning #http://127.0.0.1:8000/users/?version=v1
def get(self,request,*args,**kwargs):
print(request.version)
return HttpResponse('...')
方式2:http://127.0.0.1:8000/v3/users/
from rest_framework.views import APIView #导入 APIView
from rest_framework.versioning import QueryParameterVersioning
from rest_framework.versioning import URLPathVersioning class UsersView(APIView):
versioning_class = URLPathVersioning #http://127.0.0.1:8000/v3/users/
def get(self,request,*args,**kwargs):
print(request.version)
return HttpResponse('...')
全局配置
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'rest_framework' #组册 rest_framework APP
] REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
"VERSION_PARAM":"version", #设置默认参数名称
"DEFAULT_VERSION":'v1', #设置默认版本
"ALLOWED_VERSIONS":['v1','v2'] #设置允许的版本
}
settings配置文件
获取和反向生成url
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^(?P<version>\w+)/users/$', views.UsersView.as_view(),name='xxxxx'),
# url(r'^(?P<version>\w+)/users/$', views.UsersView.as_view()), ]
路由
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView #导入 APIView
# from rest_framework.versioning import QueryParameterVersioning
# from rest_framework.versioning import URLPathVersioning class UsersView(APIView):
# versioning_class=QueryParameterVersioning #http://127.0.0.1:8000/users/?version=v1
# versioning_class = URLPathVersioning #http://127.0.0.1:8000/v3/users/
def get(self,request,*args,**kwargs):
print(request.versioning_scheme.reverse(viewname='xxxxx',request=request)) #反向生成URL
return HttpResponse('...')
视图
2、用户身份认证
根据用户请求(tocken)做用户身份认证,成功request.user能获取到用户名,否则AnonymousUser /none;
2.1 认证源码分析
APIView 设置认证相关功能
Request类调用认证功能
class UsersView(APIView):
def get(self,request,*args,**kwargs):
self.dispatch()
print(self.authentication_classes )
return HttpResponse('...')
视图
class APIView(View):
'''1.3 去系统配置文件,获取认证相关的类s [BasicAuthentication(),SessionAuthentication()]
'''
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
#1 二次封装 request(self.initialize_request)
request = self.initialize_request(request, *args, **kwargs)
self.request = request
#替换成新的request
self.headers = self.default_response_headers # deprecate?
try:
#2、执行认证功能
self.initial(request, *args, **kwargs)
except Exception as exc:
pass
return self.response def initialize_request(self, request, *args, **kwargs): '''1.1 执行initialize_reques,执行authenticators=self.get_authenticators()
调用get_authenticators()获取[auth对象,auth对象]
'''
return Request(
request,
authenticators=self.get_authenticators(), ) def get_authenticators(self):
'''1.2 get_authenticators 去系统配置文件中获取 认证相关的类,
返回列表'''
return [auth() for auth in self.authentication_classes] #2.1 认证功能调用perform_authentication方法
def initial(self, request, *args, **kwargs):
self.perform_authentication(request)
#2.2 perform_authentication调用了 request类的user方法
def perform_authentication(self, request):
request.user
1、把 [认证对象,认证对象]封装到Request
#2、调用request的user方法,执行认证功能
class Request():
def __init__(request,authenticators=None):
self._request = request
self.authenticators = authenticators or () #or 元祖 #2.1 request.user方法调用了 _authenticat方法
@property
def user(self):
if not hasattr(self, '_user'):
self._authenticate()
return self._user def _authenticate(self): # 2.2遍历 self.authenticators,request初始环节封装的[认证对象,认证对象 ],并执行认证对象的authenticate方法
for authenticator in self.authenticators:
'''
异常终止整个循环
返回元祖,整个循环终止
返回None,进入下一次循环
'''
try:
user_auth_tuple = authenticator.authenticate(self)
except exceptions.APIException:
self._not_authenticated()
raise if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
self._not_authenticated() #2.3 如果循环出现 异常,则设置 request.user = 匿名用户
def _not_authenticated(self):
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
2、调用request的user方法,执行认证功能
2.2 使用配置
通过对 APIView 认证环节的源码分析,得知认证环节本质就是执行 BasicAuthentication、SessionAuthentication类中的authenticate方法;
如果authenticate方法执行成功 return (username,None ) ,则设置 request.user属性为username,否则设置为AnonymousUser匿名用户;
如果authenticate方法执行成功 return None ,则继续执行 下一个认证类的authenticate方法;
如果authenticate方法 raise exceptions.AuthenticationFailed('认证失败')抛出异常,则整个认证环节终止;
局部视图使用认证功能
# 源码中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK 里的东西
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
"VERSION_PARAM":"version", #设置默认参数名称
"DEFAULT_VERSION":'v1', #设置默认版本
"ALLOWED_VERSIONS":['v1','v2'] , #设置允许的版本
# 设置为 None,没有通过认证的用户,request.user匿名用户 =None
"UNAUTHENTICATED_USER":None,
# 设置为UNAUTHENTICATED_TOKEN tocken =None
'UNAUTHENTICATED_TOKEN':None
}
配置文件
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView #导入 APIView
from rest_framework.authentication import BasicAuthentication
from rest_framework.authentication import SessionAuthentication
from rest_framework.request import Request
from rest_framework import exceptions class CustomAuthentication(BasicAuthentication):
def authenticate(self, request):
return ('egon',None ) #authenticate 返回哪个用户,request.user=哪个用户
def authenticate_header(self, request):
pass class UsersView(APIView):
authentication_classes =[CustomAuthentication ]
def get(self,request,*args,**kwargs):
print(request.user)
return HttpResponse('...')
视图
自定制认证失败错误信息
from rest_framework.authentication import SessionAuthentication
from rest_framework.request import Request
from rest_framework import exceptions class CustomAuthentication(BasicAuthentication):
def authenticate(self, request):
#自定义认证失败异常信息
raise exceptions.AuthenticationFailed('认证失败')
def authenticate_header(self, request):
pass class UsersView(APIView):
authentication_classes =[CustomAuthentication ]
def get(self,request,*args,**kwargs):
print(request.user)
return HttpResponse('...')
全局视图使用认证功能
from rest_framework.authentication import BasicAuthentication
from rest_framework.authentication import SessionAuthentication
class CustomAuthentication(BasicAuthentication):
def authenticate(self, request):
return ('alex',None)
def authenticate_header(self, request):
pass
utils.py
# 源码中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK 里的东西
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
"VERSION_PARAM":"version", #设置默认参数名称
"DEFAULT_VERSION":'v1', #设置默认版本
"ALLOWED_VERSIONS":['v1','v2'] , #设置允许的版本
# 设置为 None,没有通过认证的用户,request.user匿名用户 =None
"UNAUTHENTICATED_USER":None,
# 设置为UNAUTHENTICATED_TOKEN tocken =None
'UNAUTHENTICATED_TOKEN':None,
"DEFAULT_AUTHENTICATION_CLASSES":(
"app01.utils.CustomAuthentication", ),
}
settings.py配置文件
from rest_framework.authentication import SessionAuthentication
from rest_framework import exceptions class UsersView(APIView):
def get(self,request,*args,**kwargs):
print(request.user)
# print(self.dispatch())
return HttpResponse('...')
views.py 视图
2.3 扩展
基于token的用户认证
token:服务端动态生成的1串用来检验用户身份的字符串,可以放在header、cokies、url参数(安全性较差)、请求体(CSRF token);
token和session类似,不同于 session的是token比较灵活,不仅仅可以cokies里;
from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView #导入 APIView
from rest_framework.authentication import BasicAuthentication
from rest_framework.authentication import SessionAuthentication
from rest_framework import exceptions
from rest_framework.request import Request
from rest_framework.response import Response
from app01 import models
from django.http import JsonResponse def gen_tcoken(username):
import random,hashlib,time
ctime=str(time.time())
hash=hashlib.md5(username.encode('utf-8'))
hash.update(ctime.encode('utf-8'))
return hash.hexdigest() class CustomAuthentication(BasicAuthentication): #认证类
def authenticate(self, request):
#从 url的参数中获取客户端携带的tocken
tocken=request.query_params.get('tocken')
#去数据库中检查tocken是否存在?
tocken_obj=models.Token.objects.filter(token=tocken).first()
#如果存在
if tocken_obj:
#注意还可以返回对象
return (tocken_obj.user,tocken_obj)
#否则 自定义错误信息 认证失败
raise exceptions.AuthenticationFailed('认证失败') def authenticate_header(self, request):
pass class ZhanggenAuthentication():
'''
局部使用认证功能的情况下,优先继承该类
'''
authentication_classes = [CustomAuthentication] class LoginView(APIView): #允许匿名用户访问
def post(self, request, *args, **kwargs):
ret = {'coede': 1000, 'msg': ''}
username = request.data.get('username')
pwd = request.data.get('password')
user = models.Userinfo.objects.filter(user=username, pwd=pwd)
if user:
# 根据user=user去查找,如果找到更新 如果没有找到创建defaults={} 中的数据
tk = gen_tcoken(username)
models.Token.objects.update_or_create(user=user, defaults={'token': tk})
ret['coede'] = 1001
ret['token'] = tk
else:
ret['msg'] = '用户名或者密码错误'
return JsonResponse(ret) class IndexView(ZhanggenAuthentication,APIView): #不允许匿名用户访问
def get(self,request,*args,**kwargs):
print(request.user)
print(request.user.user)
return Response('通过认证!')
from django.db import models class Userinfo(models.Model):
user=models.CharField(max_length=32)
pwd=models.CharField(max_length=64)
email=models.CharField(max_length=64)
user_type_choices=(
(1,'普通用户'),
(2,'文艺用户'),
(3,'其他用户'),
)
user_type_id=models.IntegerField(choices=user_type_choices,default=1) #设置 one to one 1个用户不能在不同设备上登录
#设置 Freikey 支持1个用户 在不同设备上同时登录
class Token(models.Model):
user=models.OneToOneField(Userinfo)
token=models.CharField(max_length=64)
models.py
def gen_tcoken(username):
import random,hashlib,time
ctime=str(time.time())
hash=hashlib.md5(username.encode('utf-8'))
hash.update(ctime.encode('utf-8'))
return hash.hexdigest()
动态Tocken生成器
class LoginView(APIView): #允许匿名用户访问
def post(self, request, *args, **kwargs):
ret = {'coede': 1000, 'msg': ''}
username = request.data.get('username')
pwd = request.data.get('password')
user = models.Userinfo.objects.filter(user=username, pwd=pwd)
if user:
# 根据user=user去查找,如果找到更新 如果没有找到创建defaults={} 中的数据
tk = gen_tcoken(username)
models.Token.objects.update_or_create(user=user, defaults={'token': tk})
ret['coede'] = 1001
ret['token'] = tk
else:
ret['msg'] = '用户名或者密码错误'
return JsonResponse(ret)
授权API接口生成Tocken并把Tocke返回给客户端。
class CustomAuthentication(BasicAuthentication): #认证类
def authenticate(self, request):
#从 url的参数中获取客户端携带的tocken
tocken=request.query_params.get('tocken')
#去数据库中检查tocken是否存在?
tocken_obj=models.Token.objects.filter(token=tocken).first()
#如果存在
if tocken_obj:
#注意还可以返回对象
return (tocken_obj.user,tocken_obj)
#否则 自定义错误信息 认证失败
raise exceptions.AuthenticationFailed('认证失败') def authenticate_header(self, request):
pass
RestApi认证类,检查客户端是否携带tocken?
class ZhanggenAuthentication():
'''
局部使用认证功能的情况下,优先继承该类
'''
authentication_classes = [CustomAuthentication] class IndexView(ZhanggenAuthentication,APIView): #不允许匿名用户访问
def get(self,request,*args,**kwargs):
print(request.user)
print(request.user.user)
return Response('通过认证!')
通过认证视图 request.user获取认证用户信息
认证失败后定制响应头
def authenticate_header(self, request):
return 'Basic realm=api'
#设置响应头,认证失败后,浏览器弹窗,再向server发送请求
authenticate_header
使用RestAPI认证功能小结
1、创建2张表userinfo 和token表
2、认证类的authenticate方法去请求头中获取token信息,然后去token表中查询token是否存在;
3、查询到token 是正常用户(返回 用户名)否则为匿名用户(raise异常终止认证、或者 return none进行下一个认证)
4、局部应用
方式1::哪个CBV需要认证在类中定义authentication_classes =[CustomAuthentication ]
方式2:额外定义1个类,CBV多继承
方式3:全局配置使用认证功能,那个CBV不使用authentication_classes =[ ]
5、全局使用 在配置文件中配置 ,注意重新创建一个模块,把认证类放里面;
3、权限相关
社会3、6、9,如何实现对API接口的访问权限设置呢?基于IP ?和用户名?
需求:普通用户对UserGroupView视图无访问权限
局部使用:
from django.db import models class Userinfo(models.Model):
user=models.CharField(max_length=32)
pwd=models.CharField(max_length=64)
email=models.CharField(max_length=64)
user_type_choices=(
(1,'普通用户'),
(2,'文艺用户'),
(3,'其他用户'),
)
user_type_id=models.IntegerField(choices=user_type_choices,default=1) #设置 one to one 1个用户不能在不同设备上登录
#设置 Freikey 支持1个用户 在不同设备上同时登录
class Token(models.Model):
user=models.OneToOneField(Userinfo)
token=models.CharField(max_length=64)
models
#权限相关
from rest_framework.permissions import BasePermission,AllowAny class CustomPermission(BasePermission):
def has_permission(self, request, view):
#view代指那个视图
#如果用户为普通用户,访问的视图是UserGroupView,没有权限拒绝访问
if request.user.user_type_id==1 and isinstance(view,UserGroupView) : return False #返回False 代表没有权限访问
return True # return True #返回True 代表都有权限访问
自定义权限类
class UserGroupView(APIView):
permission_classes=[CustomPermission]
def get(self, request, *args, **kwargs):
# print(request.user)
print(self.permission_classes)
print(request.user.user)
return Response('通过认证!')
视图应用
全局使用:
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
"VERSION_PARAM":"version", #设置默认参数名称
"DEFAULT_VERSION":'v1', #设置默认版本
"ALLOWED_VERSIONS":['v1','v2'] , #设置允许的版本
# 设置为 None,没有通过认证的用户,request.user匿名用户 =None
"UNAUTHENTICATED_USER":None,
# 设置为UNAUTHENTICATED_TOKEN tocken =None
'UNAUTHENTICATED_TOKEN':None,
"DEFAULT_AUTHENTICATION_CLASSES":(
"utils.auth.auth1.CustomAuthentication",
),
'DEFAULT_PERMISSION_CLASSES':[ "utils.permission.per1.CustomPermission",],
}
setings配置文件
#权限相关
from rest_framework.permissions import BasePermission,AllowAny
from app01 import views
class CustomPermission(BasePermission):
message='无权限访问' #定制错误信息
def has_permission(self, request, view):
#view代指那个视图
#如果用户为普通用户,访问的视图是UserGroupView,没有权限拒绝访问
if request.user.user_type_id==1 and isinstance(view,views.UserGroupView) : return False #返回False 代表没有权限访问
return True # return True #返回True 代表都有权限访问
自定制权限类
class UserGroupView(APIView):
def get(self, request, *args, **kwargs):
# print(request.user)
print(self.permission_classes)
print(request.user.user)
return Response('通过认证!')
视图
RestApi访问权限使用小结:
创建权限的类,类中has_permission(self, request, view) return flase代表没有权限访问,return True代表有权限访问,request代指请求信息、view参数代指视图
RestApi的访问权限控制工作在视图层 dispatch方法,RBAC工作在中间件;
4、限制用户访问频率
Django Rest Framework 对用户的访问频率是通过在Django缓存中记录每位用户访问时间,进行访问频率限制的;
用户访问时间记录:以用户唯一标识(IP/token)做键,访问时间戳列表做值,列表的长度为用户访问次数;
{
用户A:[ 访问时间戳1,访问时间戳2],
用户B:[ 访问时间戳1,访问时间戳2]
}
当用户A请求到来:
根据用户的唯一标识(IP/Token/用户名)获取用户A的所有访问记录时间戳列表;
获取当前时间,根据当前时间戳和访问记录列表中的时间戳、列表中时间戳个数,做对比来决定用户A是否可以继续访问;
#根据用户的唯一标识 获取当我用户的所有访问记录
self.history = self.cache.get(self.key, [])
#获取当前时间 [最近访问,最早访问 ]
self.now = self.timer() # Drop any requests from the history which have now passed the
# throttle duration
#做限制请求频率操作
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()#控制用户访问记录列表的中的访问记录,最后1个在配置时间范围内 #超出合理长度 返回False,不能继续访问
if len(self.history) >= self.num_requests:
return self.throttle_failure()
#访问记录列表在合理长度,return Ture可继续访问
return self.throttle_success()
核心源码
4.1、局部应用(基于用户对单个视图进行访问频率限制self.key = request.user.user+view.__class__.__name__)
{
用户A视图1:[ 访问时间戳1,访问时间戳2],
用户A视图2:[ 访问时间戳1,访问时间戳2]
}
# 限制访问次数相关
from rest_framework.throttling import BaseThrottle,AnonRateThrottle,SimpleRateThrottle
#根据IP
class CustomAnonThrottle(SimpleRateThrottle):
# 代指配置文件中定义的访问频率的类 {'anon':'5/m'}
scope = 'anon'
def allow_request(self, request, view):
# 已经登录管不着
if request.user:
return True
# 获取当前访问用户的唯一标识 get_cache_key是抽象方法 ip+字符串格式化
self.key = self.get_cache_key(request, view)
# 根据用户的唯一标识 获取当我用户的所有访问记录
self.history = self.cache.get(self.key, [])
# 获取当前时间 [最近访问,最早访问 ]
self.now = self.timer()
# 做限制请求频率操作
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop() # 根据配置文件 次数/时间,中的时间控制用户访问记录列表在合理的时间区间内 # 根据根据配置文件次数/时间中的次数,和列表长度决定用户是否可继续访问
# 超出合理长度 返回False,不能继续访问
if len(self.history) >= self.num_requests:
return self.throttle_failure()
# 访问记录列表在合理长度,return Ture可继续访问
return self.throttle_success() def get_cache_key(self, request, view):
return self.cache_format % {
'scope': self.scope,
'ident': self.get_ident(request)
} #根据用户名
class CustomUserThrottle(SimpleRateThrottle):
#代指配置文件中定义的访问频率的类 {'anon':'5/m'}
scope = 'user'
def allow_request(self, request, view):
#已经登录管不着
if not request.user:
return True
#获取当前访问用户的唯一标识 用户名
#self.key = request.user.user#用户对所页面进行访问频率限制
#用户对单个视图 进行访问频率限制
self.key = request.user.user+view.__class__.__name__
#根据用户的唯一标识 获取当我用户的所有访问记录
self.history = self.cache.get(self.key, [])
#获取当前时间 [最近访问,最早访问 ]
self.now = self.timer()
#做限制请求频率操作
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()#根据配置文件 次数/时间,中的时间控制用户访问记录列表在合理的时间区间内 #根据根据配置文件次数/时间中的次数,和列表长度决定用户是否可继续访问
# 超出合理长度 返回False,不能继续访问
if len(self.history) >= self.num_requests:
return self.throttle_failure()
#访问记录列表在合理长度,return Ture可继续访问
return self.throttle_success()
4.1、全局应用(基于用户对每个视图进行访问频率限制self.key = request.user.user)
{
用户A:[ 访问时间戳1,访问时间戳2],
}
url(r'^(?P<version>\w+)/usergroup/$', views.UserGroupView.as_view(), name='xxxxx'), #登录用户 4/m
url(r'^(?P<version>\w+)/test/$', views.TestView.as_view(), name='xxxxx'), #匿名用户5/m
路由系统
# 源码中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK 里的东西
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
"VERSION_PARAM":"version", #设置默认参数名称
"DEFAULT_VERSION":'v1', #设置默认版本
"ALLOWED_VERSIONS":['v1','v2'] , #设置允许的版本
# 设置为 None,没有通过认证的用户,request.user匿名用户 =None
"UNAUTHENTICATED_USER":None,
# 设置为UNAUTHENTICATED_TOKEN tocken =None
'UNAUTHENTICATED_TOKEN':None,
#认证相关配置
"DEFAULT_AUTHENTICATION_CLASSES":(
"utils.auth.auth1.CustomAuthentication",
),
#权限相关配置
'DEFAULT_PERMISSION_CLASSES':[ "utils.permission.per1.CustomPermission"],
#限制访问次数
'DEFAULT_THROTTLE_RATES':{
'anon':'5/m', #匿名用户5次
'user':'4/m', #登录用户6次
} }
setings.py配置文件
# 限制访问次数相关
from rest_framework.throttling import BaseThrottle,AnonRateThrottle,SimpleRateThrottle
#根据IP
class CustomAnonThrottle(SimpleRateThrottle):
# 代指配置文件中定义的访问频率的类 {'anon':'5/m'}
scope = 'anon'
def allow_request(self, request, view):
# 已经登录管不着
if request.user:
return True
# 获取当前访问用户的唯一标识 get_cache_key是抽象方法 ip+字符串格式化
self.key = self.get_cache_key(request, view)
# 根据用户的唯一标识 获取当我用户的所有访问记录
self.history = self.cache.get(self.key, [])
# 获取当前时间 [最近访问,最早访问 ]
self.now = self.timer()
# 做限制请求频率操作
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop() # 根据配置文件 次数/时间,中的时间控制用户访问记录列表在合理的时间区间内 # 根据根据配置文件次数/时间中的次数,和列表长度决定用户是否可继续访问
# 超出合理长度 返回False,不能继续访问
if len(self.history) >= self.num_requests:
return self.throttle_failure()
# 访问记录列表在合理长度,return Ture可继续访问
return self.throttle_success() def get_cache_key(self, request, view):
return self.cache_format % {
'scope': self.scope,
'ident': self.get_ident(request)
} #根据用户名
class CustomUserThrottle(SimpleRateThrottle):
#代指配置文件中定义的访问频率的类 {'anon':'5/m'}
scope = 'user'
def allow_request(self, request, view):
#已经登录管不着
if not request.user:
return True
#获取当前访问用户的唯一标识 用户名
self.key = request.user.user
#根据用户的唯一标识 获取当我用户的所有访问记录
self.history = self.cache.get(self.key, [])
#获取当前时间 [最近访问,最早访问 ]
self.now = self.timer()
#做限制请求频率操作
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()#根据配置文件 次数/时间,中的时间控制用户访问记录列表在合理的时间区间内 #根据根据配置文件次数/时间中的次数,和列表长度决定用户是否可继续访问
# 超出合理长度 返回False,不能继续访问
if len(self.history) >= self.num_requests:
return self.throttle_failure()
#访问记录列表在合理长度,return Ture可继续访问
return self.throttle_success()
自定义限制请求频率类
class UserGroupView(APIView):
# permission_classes = []
throttle_classes=[CustomAnonThrottle,CustomUserThrottle]
def get(self, request, *args, **kwargs):
# print(self.dispatch())
print(request.user)
# self.dispatch()
# print(self.throttle_classes)
# print(self.permission_classes)
# print(request.user.user)
# print(request.META.get('REMOTE_ADDR'))
# print(request.META.get('HTTP_X_FORWARDED_FOR'))
return Response('通过认证!') class TestView(APIView):
permission_classes = []
authentication_classes = []
throttle_classes = [CustomAnonThrottle, CustomUserThrottle]
def get(self, request, *args, **kwargs):
return Response('test通过认证!')
视图
匿名用户使用IP作为用户唯一标识
class CustomThrottle(SimpleRateThrottle):
scope = 'anon'
def get_cache_key(self, request, view):
# if request.user.is_authenticated:
# return None # Only throttle unauthenticated requests. return self.cache_format % {
'scope': self.scope,
'ident': self.get_ident(request)
}
自定义访问控制类
#限制访问次数
'DEFAULT_THROTTLE_RATES':{'anon':'5/m'}
settings配置文件
class UserGroupView(APIView):
# permission_classes = []
throttle_classes=[CustomThrottle]
def get(self, request, *args, **kwargs):
# print(request.user)
# self.dispatch()
# print(self.throttle_classes)
# print(self.permission_classes)
# print(request.user.user)
return Response('通过认证!')
视图
匿名用户5/m
url(r'^(?P<version>\w+)/test/$', views.TestView.as_view(), name='xxxxx'), #匿名用户5/m
路由
#限制访问次数
'DEFAULT_THROTTLE_RATES':{
'anon':'5/m', #匿名用户5次
'user':'4/m', #登录用户6次
}
setings.py配置文件
#根据IP
class CustomAnonThrottle(SimpleRateThrottle):
# 代指配置文件中定义的访问频率的类 {'anon':'5/m'}
scope = 'anon'
def allow_request(self, request, view):
# 已经登录管不着
if request.user:
return True
# 获取当前访问用户的唯一标识 get_cache_key是抽象方法 ip+字符串格式化
self.key = self.get_cache_key(request, view)
# 根据用户的唯一标识 获取当我用户的所有访问记录
self.history = self.cache.get(self.key, [])
# 获取当前时间 [最近访问,最早访问 ]
self.now = self.timer()
# 做限制请求频率操作
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop() # 根据配置文件 次数/时间,中的时间控制用户访问记录列表在合理的时间区间内 # 根据根据配置文件次数/时间中的次数,和列表长度决定用户是否可继续访问
# 超出合理长度 返回False,不能继续访问
if len(self.history) >= self.num_requests:
return self.throttle_failure()
# 访问记录列表在合理长度,return Ture可继续访问
return self.throttle_success() def get_cache_key(self, request, view):
return self.cache_format % {
'scope': self.scope,
'ident': self.get_ident(request)
}
自定义请求频率类
class TestView(APIView):
permission_classes = []
authentication_classes = []
throttle_classes = [CustomAnonThrottle, CustomUserThrottle]
def get(self, request, *args, **kwargs):
return Response('test通过认证!')
视图
登录用户4/m
url(r'^(?P<version>\w+)/usergroup/$', views.UserGroupView.as_view(), name='xxxxx'), #登录用户 4/m
路由
#限制访问次数
'DEFAULT_THROTTLE_RATES':{
'anon':'5/m', #匿名用户5次
'user':'4/m', #登录用户6次
}
setings配置文件
#根据用户名
class CustomUserThrottle(SimpleRateThrottle):
#代指配置文件中定义的访问频率的类 {'anon':'5/m'}
scope = 'user'
def allow_request(self, request, view):
#已经登录管不着
if not request.user:
return True
#获取当前访问用户的唯一标识 用户名
self.key = request.user.user
#根据用户的唯一标识 获取当我用户的所有访问记录
self.history = self.cache.get(self.key, [])
#获取当前时间 [最近访问,最早访问 ]
self.now = self.timer()
#做限制请求频率操作
while self.history and self.history[-1] <= self.now - self.duration:
self.history.pop()#根据配置文件 次数/时间,中的时间控制用户访问记录列表在合理的时间区间内 #根据根据配置文件次数/时间中的次数,和列表长度决定用户是否可继续访问
# 超出合理长度 返回False,不能继续访问
if len(self.history) >= self.num_requests:
return self.throttle_failure()
#访问记录列表在合理长度,return Ture可继续访问
return self.throttle_success()
自定义限制请求频率类
class UserGroupView(APIView):
# permission_classes = []
throttle_classes=[CustomAnonThrottle,CustomUserThrottle]
def get(self, request, *args, **kwargs):
# print(self.dispatch())
print(request.user)
# self.dispatch()
# print(self.throttle_classes)
# print(self.permission_classes)
# print(request.user.user)
# print(request.META.get('REMOTE_ADDR'))
# print(request.META.get('HTTP_X_FORWARDED_FOR'))
return Response('通过认证!')
4.3、练习题
要求:
登录URL: 无权限限制,要求用户登录并返回token;
首页URL:无权限限制,匿名用户限制 2/m, 登录用户 4/m ;
订单URL: 匿名用户无法查看(权限) ,登录用户 4/m;
from django.db import models from django.db import models class Userinfo(models.Model):
user=models.CharField(max_length=32)
pwd=models.CharField(max_length=64)
email=models.CharField(max_length=64)
user_type_choices=(
(1,'普通用户'),
(2,'文艺用户'),
(3,'其他用户'),
)
user_type_id=models.IntegerField(choices=user_type_choices,default=1) #设置 one to one 1个用户不能在不同设备上登录
#设置 Freikey 支持1个用户 在不同设备上同时登录
class Token(models.Model):
user=models.OneToOneField(Userinfo)
token=models.CharField(max_length=64)
数据库
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^login/',views.LoginView.as_view()),
url(r'^index/',views.IndexView.as_view()),
url(r'^order/',views.OrderView.as_view()),
]
urls.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'rest_framework' #组册 rest_framework APP
] # 源码中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK 里的东西
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
"VERSION_PARAM":"version", #设置默认参数名称
"DEFAULT_VERSION":'v1', #设置默认版本
"ALLOWED_VERSIONS":['v1','v2'] , #设置允许的版本
# 设置为 None,没有通过认证的用户,request.user匿名用户 =None
"UNAUTHENTICATED_USER":None,
# 设置为UNAUTHENTICATED_TOKEN tocken =None
'UNAUTHENTICATED_TOKEN':None,
# 权限相关配置
'DEFAULT_PERMISSION_CLASSES': ["utils.permission.permission1.CustomPermission"],
#认证相关配置
"DEFAULT_AUTHENTICATION_CLASSES":(
"utils.auth.auth1.CustomAuthentication",
),
#限制访问次数
'DEFAULT_THROTTLE_RATES':{
'anon':'2/m', #匿名用户5次
'user':'4/m', #登录用户6次
} }
配置
from rest_framework import exceptions
from rest_framework.authentication import BasicAuthentication
from app01 import models
class CustomAuthentication(BasicAuthentication): #认证类
def authenticate(self, request):
#从 url的参数中获取客户端携带的tocken
tocken=request.query_params.get('tocken')
#如果是匿名用户 url中没有携带tocken返回None不抛出异常,表示运行匿名用户访问;
if not tocken:
return (None,None)
#raise exceptions.AuthenticationFailed('认证失败') 抛出异常表示不允许匿名用户登录
#去数据库中检查tocken是否存在?
tocken_obj=models.Token.objects.filter(token=tocken).first()
#如果没有在数据中查询到用户携带tocken也允许访问
if not tocken_obj:
return (None,None)
#如果存在
return (tocken_obj.user,tocken_obj) #如果自定义认证类的authenticate方法return了(None,None),没有raise异常代表允许匿名用户访问
认证
#权限相关
from rest_framework.permissions import BasePermission,AllowAny
from app01 import views
class CustomPermission(BasePermission):
message='匿名用户无权限访问,订单页面。' #定制错误信息
def has_permission(self, request, view):
#view代指那个视图
#如果用户为普通用户,访问的视图是UserGroupView,没有权限拒绝访问
if not request.user and isinstance(view,views.OrderView) :
return False #返回False 代表没有权限访问
return True # return True #返回True 代表都有权限访问
权限
from django.shortcuts import render
from utils.throttle.throttle1 import CustomAnonThrottle,CustomUserThrottle
from rest_framework.views import APIView #导入 APIView
from rest_framework.authentication import BasicAuthentication
from rest_framework.authentication import SessionAuthentication
from app01 import models
from rest_framework.request import Request
from rest_framework.response import Response
from django.http import JsonResponse #生成动态Tocken
def gen_tcoken(username):
import random,hashlib,time
ctime=str(time.time())
hash=hashlib.md5(username.encode('utf-8'))
hash.update(ctime.encode('utf-8'))
return hash.hexdigest() ##认证相关 class LoginView(APIView):
authentication_classes = []
def get(self,request,*args,**kwargs):
ret={'code':666,'token':None,'msg':None}
username=request.GET.get('username')
# 由于API对request进行二次封装GET请获取参数可以使用request.query_params.get('pwd')
#由于API对request进行二次封装 POST获取参数可以使用request.data.get('pwd')
pwd=request.query_params.get('pwd')
user_obj=models.Userinfo.objects.filter(user=username,pwd=pwd).first()
if user_obj:
tk=gen_tcoken(username)
models.Token.objects.update_or_create(user=user_obj, defaults={'token': tk})
ret['token']=tk
else:
ret['code']=444
ret['msg']='用户名或者密码错误'
return JsonResponse(ret) class IndexView(APIView):
# 如果在全局配置了权限认证,在单个authentication_classes = []代指本视图不做认证
throttle_classes = [CustomAnonThrottle, CustomUserThrottle]
def get(self,request,*args,**kwargs):
return Response('通过主页认证') class OrderView(APIView):
'''
订单页面:用户登录之后才能访问,每分钟访问4次
认证环节:允许匿名用户
权限:不允许匿名用户对 访问 OrderView视图
节流:4/m '''
throttle_classes = [CustomUserThrottle]
def get(self,request):
return Response('通过订单页面认证')
访问频率控制+视图
5、解析器(parser)
REST framework的解析器:根据客户端发送的content-type请求头, 选择对应的解析器, 对请求体内容进行处理。
5.1源码逻辑
a、首先把self.get_parsers(姑娘)和get_content_negotiator(劳保)都封装到request对象中;
return Request(
request,
parsers=self.get_parsers(),
negotiator=self.get_content_negotiator(),
)
b、执行request.data调用解析功能negotiator根据 content_type挑选对应的parsers做解析工作
5.2:限制API接口仅处理请求头中设置content-type/json 和content-type/form的请求体
from rest_framework.parsers import JSONParser
from rest_framework.parsers import FormParser
from rest_framework.parsers import MultiPartParser
from rest_framework.negotiation import DefaultContentNegotiation
class ParserView(APIView):
parser_classes=[JSONParser,FormParser]
#只处理请求头中包含:Content - Type:application/jison和Content - Type:application/form
def get(self,request,*args,**kwargs):
return Response ('Parsing success')
def post(self,request,*args,**kwargs):
print(request.data)
# self.dispatch()
return Response('Parsing success')
接口测试:PostmanAPI接口测试工具
6、framework序列化和用户请求数据验证
后台通过Django ORM查询到数据都是QuerySet数据类型,这种数据类型不可以直接json.dumps()序列化响应给客户端,REST framework自带序列化功能,可以将数据库查询到的Foreign Key、Many to Many、Choice字段序列化为字典响应给客户端,还可以对用户提交的数据进行的验证(功能类似Form验证);
序列化
6.1:单表查询结果序列化
#自定义序列化类
from rest_framework import serializers class UserSerializers(serializers.Serializer):
user=serializers.CharField()
pwd=serializers.CharField()
email=serializers.CharField()
user_type_id=serializers.IntegerField() #应用
class SerializeView(APIView):
def get(self,request,*args,**kwargs):
user_list=models.Userinfo.objects.all()
#同时想返回序列化数据 many=True,如果是models.Userinfo.objects.all().first()使用many=Flase
ser=UserSerializers(instance=user_list,many=True)
return Response(ser.data)
单表无任何外键关系的表
6.2:source连表查询结果序列化(外键 1对多关系)
#自定义序列化类
from rest_framework import serializers class UserSerializers(serializers.Serializer):
user=serializers.CharField()
pwd=serializers.CharField()
email=serializers.CharField()
user_type_id=serializers.IntegerField()
#source代指连表操作获取数据
ug=serializers.CharField(source='ug.title') #应用
class SerializeView(APIView):
def get(self,request,*args,**kwargs):
user_list=models.Userinfo.objects.all()
#同时想返回序列化数据 many=True,如果是models.Userinfo.objects.all().first()使用many=Flase
ser=UserSerializers(instance=user_list,many=True)
return Response(ser.data)
正向连表 外键字段.xx
class TestSerializer(serializers.ModelSerializer):
#1对多字段
house=serializers.CharField(source='House.addr')
#1对多字段
car=serializers.CharField(source='car.titile')
#多对多字段
madam=serializers.CharField(source='madam.all')
car_level=serializers.CharField(source='car.get_car_level_display')
class Meta:
model=models.Person
fields=['name','house','car','madam','car_level']
外键字段.xx
source还支持反向连表查询
person=serializers.CharField(source='person_set.all')
小写表名_set().all()
depth 连表深度:自动连表,连表深度最大为10
class UserSerializers(serializers.ModelSerializer):
pwd=serializers.HyperlinkedIdentityField(view_name='xxxx')
class Meta:
model=models.Userinfo
fields='__all__'
depth=2 #自动联表
如果是数据库中普通字段在自定制serializers类中定义fileds=[‘字段名称’ ]属性即可,但是遇到choices(中文)、Many to Many字段怎么处理呢?
获取choice字段中文: get_choice字段_display
get_choices字段_display
获取多对多字段数据
方法1:
CharField()中的source参数之所以支持 外键跨表、choice字段获取中文名、多对多字段获取,原理是因为CharField类中的2个方法:
1、get_attribute 去数据库中获取值
2、to_representation 在页面上显示值
如果自己实现这2种方法,也可以自定制获取、显示外键、多对多、choice字段数据;
class MyFielf(serializers.CharField):
def get_attribute(self,instance):
#instance 代表每一行
madam_list=instance.madam.all()
return madam_list
def to_representation(self,value):
#value 就是get_attribute return的结果
ret=[]
for row in value:
ret.append({'name':row.name})
return ret class TestSerializer(serializers.ModelSerializer):
madams=MyFielf()
class Meta:
model=models.Person
fields=['name','madams']
方法2:
SerializerMethodField字段结合method_name=' 自定制方法'参数
class ModelCourseDetaiSerializer(serializers.ModelSerializer):
course_name=serializers.CharField(source='course.name') #外键
price_policy=serializers.SerializerMethodField(method_name='get_price_policyss')# 多对多
recommend_courses=serializers.SerializerMethodField(method_name='get_recommend_courses_list') # ContentType
teacher_list = serializers.SerializerMethodField(method_name='get_teacher_lists')
class Meta:
model=models.CourseDetail
fields=['id','course_name','price_policy','recommend_courses','teacher_list']
#推荐课程
def get_recommend_courses_list(self,obj):
ret=[]
courses_list=obj.recommend_courses.all()
for i in courses_list:
ret.append({'id':i.id,'name':i.name})
return ret
#价格策略
def get_price_policyss(self,obj):
ret = []
price=obj.course.price_policy.all()
for i in price:
ret.append({'id':i.id,'name':i.valid_period,'price':i.price})
return ret
#课程讲师
def get_teacher_lists(self,obj):
ret = []
teachers=obj.teachers.all()
for t in teachers:
ret.append({'name':t.name,'brief ':t.brief,'role':t.get_role_display()})
return ret class Course_DetailView(APIView):
def get(self,*args,**kwargs):
pk=kwargs.get('pk',None)
detail=models.CourseDetail.objects.get(course_id=pk)
ser = ModelCourseDetaiSerializer(instance=detail,many=False)
return Response(ser.data)
serializers.SerializerMethodField(method_name='自定制方法')
对提交到API接口的数据做校验
serializers不仅可以把后台ORM获取到的数据序列化后响应客户端,还可以对客户端提交的数据进行验证(类似Form验证功能);
#自定义序列化类
from rest_framework import serializers #复杂验证规则
class PasswordValidator(object):
def __init__(self, base):
self.base = base
def __call__(self, value):
if value != self.base:
message = '密码必须是 %s.' % self.base
raise serializers.ValidationError(message) class UserSerializers(serializers.Serializer):
user=serializers.CharField(error_messages={"required":"用户名不能为空"})
#PasswordValidator验证类
pwd=serializers.CharField(validators=[PasswordValidator(base='')])
email=serializers.CharField()
user_type_id=serializers.IntegerField()
#source代指连表操作获取数据
ug=serializers.CharField(source='ug.title') #应用
class SerializeView(APIView):
def get(self,request,*args,**kwargs):
user_list=models.Userinfo.objects.all()
#同时想返回序列化数据 many=True,如果是models.Userinfo.objects.all().first()使用many=Flase
ser=UserSerializers(instance=user_list,many=True)
return Response(ser.data) def post(self, request, *args, **kwargs):
#对用户提交数据进行验证 data=request.dat
ser = UserSerializers(data=request.data)
#验证成功返回 正确数据
if ser.is_valid():
return Response(ser.validated_data)
else:
#验证失败放回错误信息
print(ser.errors)
return Response(ser.errors)
serializers的数据验证功能类似Form验证,Form验证可以结合Models做ModelForm验证,serializers也是可以的;
class UserSerializers(serializers.ModelSerializer):
class Meta:
model=models.Userinfo
fields="__all__" #应用
class SerializeView(APIView):
def get(self,request,*args,**kwargs):
user_list=models.Userinfo.objects.all()
#同时想返回序列化数据 many=True,如果是models.Userinfo.objects.all().first()使用many=Flase
ser=UserSerializers(instance=user_list,many=True)
return Response(ser.data)
HyperlinkedIdentityField字段,(根据PK反向生成URL)
"""
Django settings for RestFrameWor练习 project. Generated by 'django-admin startproject' using Django 1.11.4. For more information on this file, see
https://docs.djangoproject.com/en/1.11/topics/settings/ For the full list of settings and their values, see
https://docs.djangoproject.com/en/1.11/ref/settings/
""" import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.11/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '#zh*tb-b3=2w^lka#47nztp=-shjy0a1j^8&pqypcaug3d%6oq' # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config',
'rest_framework' #组册 rest_framework APP
] MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
] ROOT_URLCONF = 'RestFrameWor练习.urls' TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR, 'templates'),],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
] WSGI_APPLICATION = 'RestFrameWor练习.wsgi.application' # Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
} # Password validation
# https://docs.djangoproject.com/en/1.11/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
] # Internationalization
# https://docs.djangoproject.com/en/1.11/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/1.11/howto/static-files/ STATIC_URL = '/static/' # 源码中api_settings都代指 Django setings配置文件中的REST_FRAMEWORK 里的东西
REST_FRAMEWORK = {
"DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.URLPathVersioning",#设置使用的类
"VERSION_PARAM":"version", #设置默认参数名称
"DEFAULT_VERSION":'v1', #设置默认版本
"ALLOWED_VERSIONS":['v1','v2'] , #设置允许的版本
# 设置为 None,没有通过认证的用户,request.user匿名用户 =None
"UNAUTHENTICATED_USER":None,
# 设置为UNAUTHENTICATED_TOKEN tocken =None
'UNAUTHENTICATED_TOKEN':None,
# 权限相关配置
'DEFAULT_PERMISSION_CLASSES': ["utils.permission.permission1.CustomPermission"],
#认证相关配置
"DEFAULT_AUTHENTICATION_CLASSES":(
"utils.auth.auth1.CustomAuthentication",
),
#限制访问次数
'DEFAULT_THROTTLE_RATES':{
'anon':'2/m', #匿名用户2次
'user':'4/m', #登录用户4次
} }
setings配置文件
注意如果配置文件中配置了版本控制功能,在反向生成URL的时候也要体现;
from django.conf.urls import url
from django.contrib import admin
from app01 import views
urlpatterns = [
url(r'^login/',views.LoginView.as_view()),
url(r'^index/',views.IndexView.as_view()),
url(r'^order/',views.OrderView.as_view()),
url(r'^parser/', views.ParserView.as_view()),
url(r'^serialize/',views.SerializeView.as_view()),
url(r'^serialize\.(?P<format>\w+)',views.SerializeView.as_view()),
url(r'test/(?P<pk>\d+)/(?P<version>[v1|v2]+)', views.SerializeView.as_view(),name='xxxx'),
]
urls.py
class UserSerializers(serializers.ModelSerializer):
pwd=serializers.HyperlinkedIdentityField(view_name='xxxx')
class Meta:
model=models.Userinfo
fields='__all__' #应用
class SerializeView(APIView):
def get(self,request,*args,**kwargs):
user_list=models.Userinfo.objects.all()
#同时想返回序列化数据 many=True,如果是models.Userinfo.objects.all().first()使用many=Flase
ser=UserSerializers(instance=user_list,many=True,context={'request':request})
return Response(ser.data)
视图
自动生成url字段
class UserSerializers(serializers.HyperlinkedModelSerializer):
class Meta:
model=models.Userinfo
fields=['id','user','pwd','email','url'] #应用
class SerializeView(APIView):
def get(self,request,*args,**kwargs):
user_list=models.Userinfo.objects.all()
#同时想返回序列化数据 many=True,如果是models.Userinfo.objects.all().first()使用many=Flase
ser=UserSerializers(instance=user_list,many=True,context={'request':request})
return Response(ser.data) def post(self, request, *args, **kwargs):
#对用户提交数据进行验证 data=request.dat
ser = UserSerializers(data=request.data)
#验证成功返回 正确数据
if ser.is_valid():
return Response(ser.validated_data)
else:
#验证失败放回错误信息
print(ser.errors)
return Response(ser.errors)
Serializer小结:
Serializer虽然叫序列化,却有2大功能 (序列化、Form验证)3大父类(Serializer,ModelSerializer,HyperlinkedModelSerializer);
自定义的serializers类,有3中可继承的父类;
1、serializers.Serializer:手动指定需要序列化和验证的字段
2、serializers.ModelSerializer:自动获取需要序列化和验证的字段(类似ModelFrom功能)
3、serializers.HyperlinkedModelSerializer 自动生成超链接
7、分页
当用户向我们的REST framework提交查询请求时候,怎么能把数据库里的全部数据list出来响应给用户呢?这就涉及到数据分页了,不仅要分页还要考虑分页性能;
a.根据 url参数携带的页码,进行分页;
url(r'^pager/$', views.PagerView.as_view()),
urls.py
#分页相关配置
from rest_framework.pagination import PageNumberPagination class ZhanggenPagination(PageNumberPagination):
#默认一页显示多少条数据
page_size=1
#http://127.0.0.1:8008/pager/?page=1 url参数名称
page_query_param='page'
#http://127.0.0.1:8008/pager/?page=1&page_size=2,通过url传参定制一页显示多少条数据;
page_size_query_param = 'page_size' #序列化相关配置
class PagerSerializers(serializers.ModelSerializer):
#新增一个字段
zhanggen=serializers.CharField(source='ug.title')
class Meta:
model=models.Userinfo
fields='__all__' #CBV应用分页和序列化功能
class PagerView(APIView):
def get(self,request,*args,**kwargs):
user_list=models.Userinfo.objects.all()
#实例化 自定制分页类
obj=ZhanggenPagination()
#调用分页类的paginate_queryset方法,根据url的参数 获取分页数据;
page_user_list=obj.paginate_queryset(user_list,request,self )
#序列化
ser=PagerSerializers(instance=page_user_list,many=True)
#返回结果包页码"next": "http://127.0.0.1:8008/pager/?page=2",点击跳转到下一页;
# respose=obj.get_paginated_response(ser.data)
# return respose
#返回结果不包含页码
return Response(ser.data)
视图
b. 基于某位置偏移进行分页 offset limit
urlpatterns = [url(r'^pager/$', views.PagerView.as_view()), ]
urls.py
#分页相关配置
from rest_framework.pagination import PageNumberPagination
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.pagination import CursorPagination #http://127.0.0.1:8008/pager/?offset=2&limit=5 获取3-8条(移动到2,基于2偏移获取5条数据)
class Zhanggen1Pagination(LimitOffsetPagination):
# 默认每页显示的数据条数
default_limit = 1
# URL中传入的数据位置的参数(基于这个位置偏移)
offset_query_param = 'offset'
# 偏移量参数
limit_query_param = 'limit'
# 最大每页显得条数
max_limit = None #序列化相关配置
class PagerSerializers(serializers.ModelSerializer):
#新增一个字段
zhanggen=serializers.CharField(source='ug.title')
class Meta:
model=models.Userinfo
fields='__all__' #CBV应用分页和序列化功能
class PagerView(APIView):
def get(self,request,*args,**kwargs):
user_list=models.Userinfo.objects.all()
#实例化 自定制分页类
obj=Zhanggen1Pagination()
#调用分页类的paginate_queryset方法,根据url的参数 获取分页数据;
page_user_list=obj.paginate_queryset(user_list,request,view=self )
#序列化
ser=PagerSerializers(instance=page_user_list,many=True)
#返回结果包页码"next": "http://127.0.0.1:8008/pager/?page=2",点击跳转到下一页;
respose=obj.get_paginated_response(ser.data)
return respose
#返回结果不包含页码
# return Response(ser.data)
视图
c. 游标分页
urlpatterns = [ url(r'^pager/$', views.PagerView.as_view()), ]
urls.py
#分页相关配置
from rest_framework.pagination import CursorPagination
#http://127.0.0.1:8008/pager/?cursor=cD0x
class Zhanggen2Pagination(CursorPagination):
# URL传入的游标参数
cursor_query_param = 'cursor'
# 默认每页显示的数据条数
page_size = 1
# URL传入的每页显示条数的参数
page_size_query_param = 'page_size'
# 每页显示数据最大条数
max_page_size = 1000
# 根据ID从大到小排列
ordering = "id" #序列化相关配置
class PagerSerializers(serializers.ModelSerializer):
#新增一个字段
zhanggen=serializers.CharField(source='ug.title')
class Meta:
model=models.Userinfo
fields='__all__' #CBV应用分页和序列化功能
class PagerView(APIView):
def get(self,request,*args,**kwargs):
user_list=models.Userinfo.objects.all()
#实例化 自定制分页类
obj=Zhanggen2Pagination()
#调用分页类的paginate_queryset方法,根据url的参数 获取分页数据;
page_user_list=obj.paginate_queryset(user_list,request,view=self )
#序列化
ser=PagerSerializers(instance=page_user_list,many=True)
#返回结果包页码"next": "http://127.0.0.1:8008/pager/?page=2",点击跳转到下一页;
respose=obj.get_paginated_response(ser.data)
return respose
#返回结果不包含页码
# return Response(ser.data)
视图
分页功能小结:
a. REST framework一共有3中分页方式
1、根据页码和每次能查看的条目数:http://127.0.0.1:8008/pager/?page=1&page_size=2 获取1-2条
2、基于offset偏移位置做limit获取:http://127.0.0.1:8008/pager/?offset=2&limit=5 获取3-8条(移动到2,基于2偏移获取5条数据)
3、游标分页(加密页面,1页1页得翻页)
b. 浅谈分页性能
分页方式1和方式2的缺陷:
以上分页方式最终转化成SQL语句本质是 offset和 limit
第1页:select * from 表 offset 0 limit 5
第2页:select * from 表 offset 5 limit 5
第3页:select * from 表 offset 10 limit 5
.................................
第N页:select * from 表 offset X limit 5
越往后翻 offset的值就会越大,已经翻阅过的数据量越多,需要扫描的数据就会越多,每次翻页数据库查询会把已经翻阅的数据的从头开始再扫描一遍,速度就会越慢;
解决方案:
游标分页之所以会把页码加密,只让用户点击上一页和下一页,进行1页1页的翻页。。。
原因:普通翻页和直接跳转到某页会涉及全表扫描,除非 查询数据库SQL语句中包含 id < n , id >n
翻页时记住当前页最大ID和最小ID,想要到上一页 id>min, 想要到下一页 id>max select * from 表 where id >n offset 0 limit 5
缺点:无法直接跳转到某一页
8、路由系统和视图
REST framework根据配置自动生成 增、删、改、list的url并且自动生成视图中的list、add、delete、put方法;解放你的双手!
自建路由
urlpatterns = [
#路由相关
#http://127.0.0.1:8008/router/
# 支持:GET 查询显示list页面 、 POST:增加
url(r'^router/$', views.RouterView.as_view()),
#http://127.0.0.1:8008/router.json
#支持:携带.json后缀
url(r'^router\.(?P<format>\w+)$', views.RouterView.as_view()),
#http://127.0.0.1:8008/router/1/
# 支持:put修改、delete 删除
url(r'^router/(?P<pk>\d+)/$', views.RouterView.as_view()),
#http://127.0.0.1:8008/router/1.json
#支持 get 获取单条数据
url(r'^router/(?P<pk>\d+)\.(?P<format>\w+)$', views.RouterView.as_view()), ]
urls.py
#路由相关
#路由序列化相关
class RouteSerializers(serializers.ModelSerializer):
class Meta:
model=models.Userinfo
fields='__all__' #应用
class RouterView(APIView):
def get(self,*args,**kwargs):
pk=kwargs.get('pk')
if pk:
user_obj= models.Userinfo.objects.get(pk=pk)
ser=RouteSerializers(instance=user_obj,many=False)
else:
user_list=models.Userinfo.objects.all()
ser = RouteSerializers(instance=user_list, many=True)
return Response(ser.data)
views视图
GenericAPIView内置了一些方法 完成 分页、序列化功能,无需自己实例化对象了,路由没作任何改变;
from app01 import views
urlpatterns = [ # 半自动创建路由
#http://127.0.0.1:8008/router/
# 支持:GET 查询显示list页面 、 POST:增加
url(r'^router/$', views.RouterView.as_view()),
#http://127.0.0.1:8008/router.json
#支持:携带.json后缀
url(r'^router\.(?P<format>\w+)$', views.RouterView.as_view()),
#http://127.0.0.1:8008/router/1/
# 支持:put修改、delete 删除
url(r'^router/(?P<pk>\d+)/$', views.RouterView.as_view()),
#http://127.0.0.1:8008/router/1.json
#支持 get 获取单条数据
url(r'^router/(?P<pk>\d+)\.(?P<format>\w+)$', views.RouterView.as_view()), ]
urls.py
#路由序列化相关
from rest_framework import serializers
class RouteSerializers(serializers.ModelSerializer):
class Meta:
model=models.Userinfo
fields='__all__' #路由分页相关类
from rest_framework.pagination import PageNumberPagination
class ZhanggenPagination(PageNumberPagination):
#默认一页显示多少条数据
page_size=1
#http://127.0.0.1:8008/pager/?page=1 url参数名称
page_query_param='page'
#http://127.0.0.1:8008/pager/?page=1&page_size=2,通过url传参定制一页显示多少条数据;
page_size_query_param = 'page_size' #半自动路由 from rest_framework.generics import GenericAPIView
class RouterView(GenericAPIView):
#数据库数据
queryset = models.Userinfo.objects.all()
#序列化类
serializer_class = RouteSerializers
#分页类
pagination_class = ZhanggenPagination
def get(self,request,*args,**kwargs):
#获取数据库数据
user_list=self.get_queryset()
#获取分页相关,对数获取到的数据进行分页
page_user_list=self.paginate_queryset(user_list)
#获取序列化相关,对已经分页得数据库数据进行序列化
ser=self.get_serializer(instance=page_user_list,many=True)
respose=self.get_paginated_response(ser.data)
return respose
views.py
半自动路由: as_view({'get': "retrieve", "put": 'update','delete':'destroy'})) 更加as.view方法中的参数,自动触发视图执行
from app01 import views
urlpatterns = [
#半自动路由:根据不同的请求自动触发 不同的视图做不同的操作
url(r'^router/$',views.RouterView.as_view({'get':"list","post":'create' } )),
#http://127.0.0.1:8008/router/1/
#get请求获取某条数据 put请求某条数据 delete请求删除单条数据
url(r'^router/(?P<pk>\d+)/$', views.RouterView.as_view({'get': "retrieve", "put": 'update','delete':'destroy'})),
]
urls.py
#路由序列化相关
from rest_framework import serializers
class RouteSerializers(serializers.ModelSerializer):
class Meta:
model=models.Userinfo
fields='__all__' from rest_framework.viewsets import ModelViewSet
class RouterView(ModelViewSet):
#数据库数据
queryset = models.Userinfo.objects.all()
#序列化类
serializer_class = RouteSerializers
视图
全自动路由
from django.conf.urls import url,include
from app01 import views
from rest_framework.routers import DefaultRouter router=DefaultRouter()
#http://127.0.0.1:8008/zhanggen/2/ 设置url前缀对应的视图
router.register('zhanggen',views.RouterView) urlpatterns = [ url(r'^',include(router.urls)), ]
urls.py
#路由序列化相关
class RouteSerializers(serializers.ModelSerializer):
class Meta:
model=models.Userinfo
fields='__all__' from rest_framework.viewsets import ModelViewSet
from rest_framework import mixins
class RouterView(ModelViewSet):
#数据库数据
queryset = models.Userinfo.objects.all()
#序列化类
serializer_class = RouteSerializers
视图
framework视图类总结
继承最底层APIView:功能少,可定制性强
继承ModelViewSet:功能丰富,FBV里list、add、delet..方法可定制性查;
9、渲染
REST framework根据用户请求url后缀(http://127.0.0.1:8008/render.json,http://127.0.0.1:8008/render/form/)的不同,渲染出不同的数据格式响应给客户端;
注意:后端渲染功能是结合路由系统中设置format传入的参数来做渲染的,所有不要忘了在路由系统中设置format来接收url传来的参数;
urlpatterns = [ #渲染相关
url(r'^render/$', views.RenderView.as_view()),
#http://127.0.0.1:8008/render.json
url(r'^render\.(?P<format>\w+)$', views.RenderView.as_view()),
#http://127.0.0.1:8008/render/json/
url(r'^render/(?P<format>\w+)/$', views.RenderView.as_view()), ]
路由系统
#渲染相关
from rest_framework.renderers import JSONRenderer,AdminRenderer,BrowsableAPIRenderer,HTMLFormRenderer
#序列化
class RenderSerializers(serializers.ModelSerializer):
class Meta:
model=models.Userinfo
fields='__all__' # class RenderView(APIView):
# #支持响应 json、admin 格式的数据
# renderer_classes =[JSONRenderer,AdminRenderer,BrowsableAPIRenderer]
# def get(self,request,*args,**kwargs):
# user_list=models.Userinfo.objects.all()
# ser=RenderSerializers(instance=user_list,many=True)
# return Response(ser.data) class RenderView(APIView):
#支持响应form格式的数据
renderer_classes =[HTMLFormRenderer]
def get(self,request,*args,**kwargs):
#注意返回form格式的数据instance必须为单个对象
user_list=models.Userinfo.objects.all().first()
ser=RenderSerializers(instance=user_list,many=False)
return Response(ser.data)
视图
使用RET framework开展项目总结
0、RET framework 内置了 版本、用户认证、访问权限、限制访问频率公共功能;(全局使用在配置文件配置,在全局使用的前提下,局部视图使用 xxclass=[ ]代表局部不使用)
1、虽然使用全自动url结合ModelViewSet视图可以快速搭建一个简单的API,但是慎用因为后期项目扩展和更新很难修改;
class Zhanggen(APIView): #继承APIView可扩展性强
def get(self, request, *args, **kwargs):
pass def post(self, request, *args, **kwargs):
pass def put(self, request, *args, **kwargs):
pass def delete(self, request, *args, **kwargs):
pass
2、1个视图对应4个标准url
#http://127.0.0.1:8008/router/
# 支持:GET 查询显示list页面 、 POST:增加
url(r'^router/$',views.RouterView.as_view()),
#http://127.0.0.1:8008/router.json
#支持:携带.json后缀
url(r'^router\.(?P<format>\w+)$', views.RouterView.as_view()),
#http://127.0.0.1:8008/router/1/
# 支持:put修改、delete 删除
url(r'^router/(?P<pk>\d+)/$', views.RouterView.as_view()),
#http://127.0.0.1:8008/router/1.json
#支持 get 获取单条数据
url(r'^router/(?P<pk>\d+)\.(?P<format>\w+)$', views.RouterView.as_view()),
3、版本控制
url(r'test/(?P<pk>\d+)/(?P<version>[v1|v2]+)$', views.SerializeView.as_view(),name='xxxx'),
银角大王博客:http://www.cnblogs.com/wupeiqi/articles/7805382.html
Django之REST framework源码分析的更多相关文章
- 3---Django rest framework源码分析(3)----节流
Django rest framework源码分析(3)----节流 目录 添加节流 自定义节流的方法 限制60s内只能访问3次 (1)API文件夹下面新建throttle.py,代码如下: # u ...
- Django rest framework源码分析(3)----节流
目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...
- Django rest framework源码分析(1)----认证
目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...
- Django rest framework 源码分析 (1)----认证
一.基础 django 2.0官方文档 https://docs.djangoproject.com/en/2.0/ 安装 pip3 install djangorestframework 假如我们想 ...
- Django rest framework源码分析(一) 认证
一.基础 最近正好有机会去写一些可视化的东西,就想着前后端分离,想使用django rest framework写一些,顺便复习一下django rest framework的知识,只是顺便哦,好吧. ...
- Python学习---Django的request.post源码分析
request.post源码分析: 可以看到传递json后会帮我们dumps处理一次最后一字节形式传递过去
- Django rest framework源码分析(4)----版本
版本 新建一个工程Myproject和一个app名为api (1)api/models.py from django.db import models class UserInfo(models.Mo ...
- Django rest framework源码分析(2)----权限
目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...
- Django中间件之SessionMiddleware源码分析
settings.py文件中 MIDDLEWARE = [ 'django.contrib.sessions.middleware.SessionMiddleware', ] # from djang ...
随机推荐
- 51nod 1437 迈克步(单调栈)
http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1437 题意: 思路: 单调栈题.求出以每个数为区间最大值的区间范围即可. ...
- git服务器搭建全程
为了后续安装能正常进行,我们先来安装一些相关依赖库和编译工具 [root@VM_95_113_centos ~]# yum install curl-devel expat-devel gettext ...
- Jmeter工具
开源,纯java,用于HTTP协议性能测试,接口协议测试工具,自动化测试工具 功能测试角度: 1.支持多种不同类型的协议.接口协议有多种 2.可以全面支持HTTP协议(后台交互) 3.其他非直接支持的 ...
- Python lambda 表达式
def ds(x): *x+ print(ds()) g = lambda x:*x+ print(g()) #------------ def sf(a,b): return a+b print(s ...
- 真刀实战地搭建React+Webpack+Express搭建一个简易聊天室
一.前面bb两句 因为自惭(自残)webpack配置还不够熟悉,想折腾着做一个小实例熟悉.想着七夕快到了,做一个聊天室自己和自己聊天吧哈哈.好了,可以停止bb了,说一下干货. 二. 这个项目能学到啥? ...
- mysql Percona-XtraBackup backup
全量备份FULL BACKUP https://www.percona.com/doc/percona-xtrabackup/2.4/backup_scenarios/full_backup.html ...
- SqlServer中常常搞不清楚 sp_columns来看一看
The sp_columns catalog stored procedure is equivalent to SQLColumns in ODBC. The results returned ar ...
- log4net配置使用
1.配置文件 app.config <?xml version="1.0" encoding="utf-8" ?> <configuratio ...
- 微信小程序 数据绑定方式
与vue不同,在微信小程序中,js的数据和前端显示的数据是单数据流,也就是说,js里边的数据变了(通过setData),前端能立刻显示.但如果前端数据变了,js中的变量不能改变. 这个相比传统的前端已 ...
- django核心配置项
Django的默认配置文件中,包含上百条配置项目,其中很多是我们‘一辈子’都不碰到或者不需要单独配置的,这些项目在需要的时候再去查手册. 强调:配置的默认值不是在settings.py文件中!不要以为 ...