Django Rest Framework 版本控制

前言

通过DRF写的视图类,都会继承APIVIEW,而APIVIEW类继承VIEW,同时其重写了as_view方法【使之cbv能够url传参】

dispatch方法,重新封装了request对象

将老的request对象赋值为_request

前端传过来的所有数据给了:request.data

之前的request.GET封装成:request.query_params

版本控制是做什么用的, 我们为什么要用?

开发是有周期,及版本迭代的,新版本上线,老版本也不能直接拉下,需要进行维护

版本控制是怎么实现的

version版本信息 赋值给了 request.version

版本控制方案 赋值给了 request.versioning_scheme

从而之后我们在视图中就可以通过request点属性,操作版本信息了

其实这个版本控制方案~就是我们配置的版本控制的类~~

也就是说,APIView通过这个方法初始化自己提供的组件~~

DRF提供了5种版本控制类的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from rest_framework import versioning

#最基础的版本控制类,给其他版本控制类提供一些共用方法
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))

# 在accept请求头中配置版本信息
# accept代表希望返回的数据类型 可以携带版本信息
# Accept: application/json; version=1.0
class AcceptHeaderVersioning(BaseVersioning): ...
pass
# 在url上携带版本信息
# url(r'^(?P<version>[v1|v2]+)/users/$',users_list,name='users-list')
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

# 把版本信息放在路由分发里,并把路由的namespace配置成版本
# url('^v1/', include('users.urls', namespace='v1'))
class NamespaceVersioning(BaseVersioning):
pass

# 在我们的host上配置版本信息
# Host: v1.example.com
class HostNameVersioning(BaseVersioning):
pass

# 在我们url上过滤条件上配置版本信息
# GET /something/?version=0.1 HTTP/1.1
class QueryParameterVersioning(BaseVersioning):
pass

版本配置如何使用

不管基于那种方式都需要配置settings

setting配置

1
2
3
4
5
REST_FRAMEWORK = {
'DEFAULT_VERSION': 'v1', # 默认版本
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version' # URL中获取值的key
}

基于url的get传参方式

/users?verson=v1

urls.py

1
2
3
4
5
from web.views import TestView

urlpatterns = [
url(r'^test/', TestView.as_view(),name='test'), #不同的方式此处路由方式不同
]

Views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/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请求,响应内容')

基于url的正则方式

/v1/users/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# urls.py

from web.views import TestView

urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/test/', TestView.as_view(), name='test'),
]

# views.py

from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning

class TestView(APIView):
versioning_class = URLPathVersioning

基于主机名方法

v1.example.com

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# urls.py
from web.views import TestView

urlpatterns = [
url(r'^test/', TestView.as_view(), name='test'),
]

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import URLPathVersioning

class TestView(APIView):
versioning_class = HostNameVersioning

基于django路由系统的namespace

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# urls.py
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')),
]

# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import NamespaceVersioning


class TestView(APIView):
versioning_class = NamespaceVersioning