Django rest framework 版本控制(源码分析)
基于上述分析
#2.处理版本信息 处理认证信息 处理权限信息 对用户的访问频率进行限制
self.initial(request, *args, **kwargs)
#2.1处理版本信息
#version代表版本 scheme代表版本管理的类 determine_version返回的是一个元祖
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
进入determine_version方法
def determine_version(self, request, *args, **kwargs):
"""
If versioning is being used, then determine any API version for the
incoming request. Returns a two-tuple of (version, versioning_scheme)
"""
#versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
#如果我们没有配置版本控制类,将不做版本控制
if self.versioning_class is None:
return (None, None) #scheme就是版本控制的类
#versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
scheme = self.versioning_class()
#返回版本 和版本控制的类
return (scheme.determine_version(request, *args, **kwargs), scheme)
scheme.determine_version的执行取决与我们所引用的版本控制类是哪一个

这里以常用的URLPathVersioning类来说明
class URLPathVersioning(BaseVersioning):
"""
To the client this is the same style as `NamespaceVersioning`.
The difference is in the backend - this implementation uses
Django's URL keyword arguments to determine the version. An example URL conf for two views that accept two different versions. urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
url(r'^(?P<version>[v1|v2]+)/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')
] GET /1.0/something/ HTTP/1.1
Host: example.com
Accept: application/json
"""
invalid_version_message = _('Invalid version in URL path.') def determine_version(self, request, *args, **kwargs):
#version_param default_version DEFAULT_VERSION 都是在settings配置
#version_param :url中获取值的key
#default_version :默认版本
#ALLOWED_VERSIONS:允许的版本
version = kwargs.get(self.version_param, self.default_version)
#如果version不存在,或者version不在允许访问的版本列表中
if not self.is_allowed_version(version):
#抛出异常
raise exceptions.NotFound(self.invalid_version_message)
#返回版本
return version
is_allowed_version方法
#判断是否能访问当前版本
def is_allowed_version(self, version):
if not self.allowed_versions:
return True
#version存在并且等于默认版本或者version在允许访问的版本中
#返回True or Fasle
return ((version is not None and version == self.default_version) or
(version in self.allowed_versions))
到这里我们获取到了具体访问的版本和控制版本的类
回到最开始
#2.1处理版本信息
#version代表版本 scheme代表版本管理的类 determine_version返回的是一个元祖
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
我们将version 和scheme封装在request中如果我们访问的版本符合要求我们可以通过调用
request.version, request.versioning_scheme 来获得版本号和控制版本的类
BaseVersioning所有版本控制类都要继承的基类
class BaseVersioning(object):
default_version = api_settings.DEFAULT_VERSION
allowed_versions = api_settings.ALLOWED_VERSIONS
version_param = api_settings.VERSION_PARAM def determine_version(self, request, *args, **kwargs):
msg = '{cls}.determine_version() must be implemented.'
raise NotImplementedError(msg.format(
cls=self.__class__.__name__
)) def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
return _reverse(viewname, args, kwargs, request, format, **extra) #判断是否能访问当前版本
def is_allowed_version(self, version):
if not self.allowed_versions:
return True
#version存在并且等于默认版本或者version在允许访问的版本中
#返回True or Fasle
return ((version is not None and version == self.default_version) or
(version in self.allowed_versions))
BaseVersioning
例子
a. 基于url的get传参方式(应用QueryParameterVersioning)
class QueryParameterVersioning(BaseVersioning):
"""
GET /something/?version=0.1 HTTP/1.1
Host: example.com
Accept: application/json
"""
invalid_version_message = _('Invalid version in query parameter.') def determine_version(self, request, *args, **kwargs):
version = request.query_params.get(self.version_param, self.default_version)
if not self.is_allowed_version(version):
raise exceptions.NotFound(self.invalid_version_message)
return version def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
url = super(QueryParameterVersioning, self).reverse(
viewname, args, kwargs, request, format, **extra
)
if request.version is not None:
return replace_query_param(url, self.version_param, request.version)
return url
QueryParameterVersioning
如:/users?version=v1
REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}
settings
from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view(),name='test'),
] urls.py
urls
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning class TestView(APIView):
versioning_class = QueryParameterVersioning def get(self, request, *args, **kwargs): # 获取版本
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme) # 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')
views.py
b. 基于url的正则方式
如:/v1/users/
REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}
settings
from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'),
]
urls
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning class TestView(APIView):
versioning_class = URLPathVersioning def get(self, request, *args, **kwargs):
# 获取版本
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme) # 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')
views.py
这种方式传参url写法源码有说明(URLPathVersioning):
class BaseVersioning(object):
default_version = api_settings.DEFAULT_VERSION
allowed_versions = api_settings.ALLOWED_VERSIONS
version_param = api_settings.VERSION_PARAM def determine_version(self, request, *args, **kwargs):
msg = '{cls}.determine_version() must be implemented.'
raise NotImplementedError(msg.format(
cls=self.__class__.__name__
)) def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
return _reverse(viewname, args, kwargs, request, format, **extra) #判断是否能访问当前版本
def is_allowed_version(self, version):
if not self.allowed_versions:
return True
#version存在并且等于默认版本或者version在允许访问的版本中
#返回True or Fasle
return ((version is not None and version == self.default_version) or
(version in self.allowed_versions))
BaseVersioning
class URLPathVersioning(BaseVersioning):
"""
To the client this is the same style as `NamespaceVersioning`.
The difference is in the backend - this implementation uses
Django's URL keyword arguments to determine the version. An example URL conf for two views that accept two different versions. urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/users/$', users_list, name='users-list'),
url(r'^(?P<version>[v1|v2]+)/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')
] GET /1.0/something/ HTTP/1.1
Host: example.com
Accept: application/json
"""
invalid_version_message = _('Invalid version in URL path.') def determine_version(self, request, *args, **kwargs):
#version_param default_version DEFAULT_VERSION 都是在settings配置
#version_param :url中获取值的key
#default_version :默认版本
#ALLOWED_VERSIONS:允许的版本
version = kwargs.get(self.version_param, self.default_version)
#如果version不存在,或者version不在允许访问的版本列表中
if not self.is_allowed_version(version):
#抛出异常
raise exceptions.NotFound(self.invalid_version_message)
#返回版本
return version def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
if request.version is not None:
kwargs = {} if (kwargs is None) else kwargs
kwargs[self.version_param] = request.version return super(URLPathVersioning, self).reverse(
viewname, args, kwargs, request, format, **extra
)
URLPathVersioning
c. 基于 accept 请求头方式
如:Accept: application/json; version=1.0
REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}
settings
from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view(), name='test'),
]
urls
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import AcceptHeaderVersioning class TestView(APIView):
versioning_class = AcceptHeaderVersioning def get(self, request, *args, **kwargs):
# 获取版本 HTTP_ACCEPT头
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme)
# 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')
views.py
基于版本控制类AcceptHeaderVersioning
class AcceptHeaderVersioning(BaseVersioning):
"""
GET /something/ HTTP/1.1
Host: example.com
Accept: application/json; version=1.0
"""
invalid_version_message = _('Invalid version in "Accept" header.') def determine_version(self, request, *args, **kwargs):
media_type = _MediaType(request.accepted_media_type)
version = media_type.params.get(self.version_param, self.default_version)
version = unicode_http_header(version)
if not self.is_allowed_version(version):
raise exceptions.NotAcceptable(self.invalid_version_message)
return version # We don't need to implement `reverse`, as the versioning is based
# on the `Accept` header, not on the request URL.
AcceptHeaderVersioning
d. 基于主机名方法
如:v1.example.com
ALLOWED_HOSTS = ['*']
REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}
settings.py
from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^test/', TestView.as_view(), name='test'),
]
urls.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import HostNameVersioning class TestView(APIView):
versioning_class = HostNameVersioning def get(self, request, *args, **kwargs):
# 获取版本
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme)
# 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')
views.py
引用的的版本控制类
class HostNameVersioning(BaseVersioning):
"""
GET /something/ HTTP/1.1
Host: v1.example.com
Accept: application/json
"""
hostname_regex = re.compile(r'^([a-zA-Z0-9]+)\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$')
invalid_version_message = _('Invalid version in hostname.') def determine_version(self, request, *args, **kwargs):
hostname, separator, port = request.get_host().partition(':')
match = self.hostname_regex.match(hostname)
if not match:
return self.default_version
version = match.group(1)
if not self.is_allowed_version(version):
raise exceptions.NotFound(self.invalid_version_message)
return version # We don't need to implement `reverse`, as the hostname will already be
# preserved as part of the REST framework `reverse` implementation.
HostNameVersioning
e. 基于django路由系统的namespace
如:example.com/v1/users/
REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}
settings
from django.conf.urls import url, include
from web.views import TestView urlpatterns = [
url(r'^v1/', ([
url(r'test/', TestView.as_view(), name='test'),
], None, 'v1')),
url(r'^v2/', ([
url(r'test/', TestView.as_view(), name='test'),
], None, 'v2')), ]
urls.py
#!/usr/bin/env python
# -*- coding:utf-8 -*-
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import NamespaceVersioning class TestView(APIView):
versioning_class = NamespaceVersioning def get(self, request, *args, **kwargs):
# 获取版本
print(request.version)
# 获取版本管理的类
print(request.versioning_scheme)
# 反向生成URL
reverse_url = request.versioning_scheme.reverse('test', request=request)
print(reverse_url) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs):
return Response('POST请求,响应内容') def put(self, request, *args, **kwargs):
return Response('PUT请求,响应内容')
views.py
引用的版本控制类NamespaceVersioning
class NamespaceVersioning(BaseVersioning):
"""
To the client this is the same style as `URLPathVersioning`.
The difference is in the backend - this implementation uses
Django's URL namespaces to determine the version. An example URL conf that is namespaced into two separate versions # users/urls.py
urlpatterns = [
url(r'^/users/$', users_list, name='users-list'),
url(r'^/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')
] # urls.py
urlpatterns = [
url(r'^v1/', include('users.urls', namespace='v1')),
url(r'^v2/', include('users.urls', namespace='v2'))
] GET /1.0/something/ HTTP/1.1
Host: example.com
Accept: application/json
"""
invalid_version_message = _('Invalid version in URL path. Does not match any version namespace.') def determine_version(self, request, *args, **kwargs):
resolver_match = getattr(request, 'resolver_match', None)
if resolver_match is None or not resolver_match.namespace:
return self.default_version # Allow for possibly nested namespaces.
possible_versions = resolver_match.namespace.split(':')
for version in possible_versions:
if self.is_allowed_version(version):
return version
raise exceptions.NotFound(self.invalid_version_message) def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):
if request.version is not None:
viewname = self.get_versioned_viewname(viewname, request)
return super(NamespaceVersioning, self).reverse(
viewname, args, kwargs, request, format, **extra
) def get_versioned_viewname(self, viewname, request):
return request.version + ':' + viewname
NamespaceVersioning
Django rest framework 版本控制(源码分析)的更多相关文章
- Python学习---Django的request.post源码分析
request.post源码分析: 可以看到传递json后会帮我们dumps处理一次最后一字节形式传递过去
- Django Rest Framework框架源码流程
在详细说django-rest-framework源码流程之前,先要知道什么是RESTFUL.REST API . RESTFUL是所有Web应用都应该遵守的架构设计指导原则. REST是Repres ...
- Django中间件之SessionMiddleware源码分析
settings.py文件中 MIDDLEWARE = [ 'django.contrib.sessions.middleware.SessionMiddleware', ] # from djang ...
- Django _VIEW视图_源码分析
Django _VIEW视图: 1. 点击as_view方法. 第二步: as_view () 为VIEW 类里定义的,到时候我们定义业务逻辑的类就继承这个VIEW类. view方法内返回的是disp ...
- Django之CBV视图源码分析(工作原理)
1.首先我们先在urls.py定义CBV的路由匹配. FBV的路由匹配: 2.然后,在views.py创建一名为MyReg的类: 注意:该类必须继续View类,且方法名必须与请求方式相同(后面会详解) ...
- ABP源码分析一:整体项目结构及目录
ABP是一套非常优秀的web应用程序架构,适合用来搭建集中式架构的web应用程序. 整个Abp的Infrastructure是以Abp这个package为核心模块(core)+15个模块(module ...
- Django_Restframwork_APIVIEW视图_源码分析
Django _VIEW视图_源码分析
- Django之REST framework源码分析
前言: Django REST framework,是1个基于Django搭建 REST风格API的框架: 1.什么是API呢? API就是访问即可获取数据的url地址,下面是一个最简单的 Djang ...
- Django rest framework源码分析(3)----节流
目录 Django rest framework(1)----认证 Django rest framework(2)----权限 Django rest framework(3)----节流 Djan ...
随机推荐
- 线程同步(使用了synchronized)和线程通讯(使用了wait,notify)
线程同步 什么是线程同步? 当使用多个线程来访问同一个数据时,非常容易出现线程安全问题(比如多个线程都在操作同一数据导致数据不一致),所以我们用同步机制来解决这些问题. 实现同步机制有两个方法:1.同 ...
- OBJ文件
OBJ文件是Alias|Wavefront公司为它的一套基于工作站的3D建模和动画软件"Advanced Visualizer"开发的一种标准3D模型文件格式,很适合用于3D软件模 ...
- usebean 使用语法
- hdu 2768 Cat vs. Dog (二分匹配)
Cat vs. Dog Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total ...
- Elasticsearch 更新索引settings
1.更新索引设置:将副本减至0,修改索引分析器为ik_max_word和检索分词器为ik_smart 2.需要先将索引关闭,然后再PUT setings POST user/_close PUT us ...
- PCA误差
我们知道,PCA是用于对数据做降维的,我们一般用PCA把m维的数据降到k维(k < m). 那么问题来了,k取值多少才合适呢? PCA误差 PCA的原理是,为了将数据从n维降低到k维,需要找到k ...
- HTTPS证书生成方法,也适用于APP
步骤: 一.登录服务器,创建ioscert目录 cd ~/servers/APP mkdir ioscert 二.cd到ioscert目录下,执行以下命令: 1.openssl genrsa -out ...
- [Leetcode] Binary tree postorder traversal二叉树后序遍历
Given a binary tree, return the postorder traversal of its nodes' values. For example:Given binary t ...
- BZOJ1041 [HAOI2008]圆上的整点 【数学】
1041: [HAOI2008]圆上的整点 Time Limit: 10 Sec Memory Limit: 162 MB Submit: 4631 Solved: 2087 [Submit][S ...
- Redis、Memcache
★ Redis redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sor ...