版本
  # 链接资料
# 1. 版本控制介绍
API版本控制允许我们在不同的客户端之间更改行为(同一个接口的不同版本会返回不同的数据)。DRF提供了许多不同的版本控制方案。可能会有一些客户端因为某些原因已经不再维护了,但是我们后端的接口还是要不断的更新迭代的,这个时候通过版本控制返回不同的内容就是一种不错的解决方案。
其实说白了,我访问的是同一个视图,但是由于不同的版本,返回的参数是不一样的。那这个是怎么控制的呐?来我们继续往下介绍。
主要是配置settings.py版本信息,然后在参考把版本信息是反正url,还是请求头.....哪里哪里。。
# 2. 自定义版本
自定义:
				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)
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 2.1 使用自带的版本
# 使用:
配置文件:
REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',  # 版本信息
    "DEFAULT_VERSION": 'v1',  # 默认版本号,默认值为None
    "ALLOWED_VERSIONS": ['v1', 'v2'],  # 允许请求的版本号,默认值为None
    "VERSION_PARAM": 'version',  # 识别版本号参数的名称,默认值为'version'
}
路由:
urlpatterns = [ # 主路由
    url(r'^api/', include('api.urls')),
]
urlpatterns = [
    # http://127.0.0.1:8000/api/v1/users/
    # http://127.0.0.1:8000/api/v2/users/
    url(r'^(?P<version>[v1|v2]+)/users/$', views.UsersView.as_view(),name='uuu'),
]
                    
# views.py                    
class UsersView(APIView):
    permission_classes = []
    def get(self, request, *args, **kwargs):
        # 1. 获取版本  v1
        print(request.version) 
        # 2. 获取处理版本的对象 <rest_framework.versioning.URLPathVersioning object at 0x000001C3AA7D3E10>
        print(request.versioning_scheme)  
        # 3. 反向生成URL(rest framework) http://127.0.0.1:8000/api/v1/users/
        u1 = request.versioning_scheme.reverse(viewname='uuu', request=request)
        print(u1)
        # 4. 反向生成URL /api/2/users/
        u2 = reverse(viewname='uuu', kwargs={'version': 2})
        print(u2)
        return HttpResponse('用户列表')
 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
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
# 注意
在需要获取请求的版本号时,可以通过request.version来获取版本号。
默认版本功能未开启,request.version 返回None。
开启版本支持功能,需要在配置文件中设置DEFAULT_VERSIONING_CLASS
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',  # 版本信息
 1
其他可选配置:
- DEFAULT_VERSION 默认版本号,默认值为None
 - ALLOWED_VERSIONS 允许请求的版本号,默认值为None
 - VERSION_PARAM 识别版本号参数的名称,默认值为'version'
 
request.version
request.versioning_scheme
request.versioning_scheme.reverse(viewname='uuu', request=request)
reverse(viewname='uuu', kwargs={'version': 2})
 1
2
3
4
2
3
4
# 3. 更多支持的版本处理方式
# AcceptHeaderVersioning
请求头中传递的Accept携带version版本信息
GET /bookings/ HTTP/1.1
Host: example.com
Accept: application/json; version=1.0
 1
2
3
2
3
# URLPathVersioning(推荐使用)
URL路径中携带版本信息
urlpatterns = [
    url( r'^(?P<version>(v1|v2))/bookings/$', bookings_list,name='bookings-list' ),
    url( r'^(?P<version>(v1|v2))/bookings/(?P<pk>[0-9]+)/$',bookings_detail,name='bookings-detail')
]
 1
2
3
4
2
3
4
# NamespaceVersioning
命名空间中定义
# bookings/urls.py
urlpatterns = [
    url(r'^$', bookings_list, name='bookings-list'),
    url(r'^(?P<pk>[0-9]+)/$', bookings_detail, name='bookings-detail')
]
# urls.py
urlpatterns = [
    url(r'^v1/bookings/', include('bookings.urls', namespace='v1')),
    url(r'^v2/bookings/', include('bookings.urls', namespace='v2'))
]
 1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
# HostNameVersioning
主机域名携带 版本信息
GET /bookings/ HTTP/1.1
Host: v1.example.com
Accept: application/json
 1
2
3
2
3
# QueryParameterVersioning
查询字符串携带 版本信息
GET /something/?version=0.1 HTTP/1.1
Host: example.com
Accept: application/json
 1
2
3
2
3
# 示例
REST_FRAMEWORK = {
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.QueryParameterVersioning'
}
class BookInfoSerializer(serializers.ModelSerializer):
    """图书数据序列化器"""
    class Meta:
        model = BookInfo
        fields = ('id', 'btitle', 'bpub_date', 'bread', 'bcomment')
class BookInfoSerializer2(serializers.ModelSerializer):
    """图书数据序列化器"""
    class Meta:
        model = BookInfo
        fields = ('id', 'btitle', 'bpub_date')
class BookDetailView(RetrieveAPIView):
    queryset = BookInfo.objects.all()
    def get_serializer_class(self):
        if self.request.version == '1.0':
            return BookInfoSerializer
        else:
            return BookInfoSerializer2
# 127.0.0.1:8000/books/2/
# 127.0.0.1:8000/books/2/?version=1.0
 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
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
# 源码流程
# 1.APIView下的dispatch方法
    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
        # 获取的参数
        # 把获取的参数,经过initialize_request构建成新的request
        request = self.initialize_request(request, *args, **kwargs)  # 封装requert
        self.request = request
        self.headers = self.default_response_headers  # deprecate?
        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
 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
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
# 2. 进入认证self.initial(request, *args, **kwargs)
    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
        self.format_kwarg = self.get_format_suffix(**kwargs)
        # Perform content negotiation and store the accepted info on the request
        neg = self.perform_content_negotiation(request)
        request.accepted_renderer, request.accepted_media_type = neg
        # Determine the API version, if versioning is in use.
        # 版本信息
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme
        # Ensure that the incoming request is permitted
        # 实现认证
        self.perform_authentication(request)
        # 权限判断
        self.check_permissions(request)
        # 限流
        self.check_throttles(request)
 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 进入 self.determine_version(request, *args, **kwargs)
    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)
        """
        if self.versioning_class is None:
            return (None, None)
        scheme = self.versioning_class()  # 获取是那种的版本信息方式
        return (scheme.determine_version(request, *args, **kwargs), scheme)  # 重点是这个determine_version
 1
2
3
4
5
6
7
8
9
10
2
3
4
5
6
7
8
9
10
编辑  (opens new window)
  ← 接口访问控制(限流) 分页→