Django REST Framework 版本控制

 01-版本控制

对接口进行版本控制只是一种杀死已部署客户端的“礼貌”方式。
- 罗伊菲尔丁。

1. API版本控制允许您更改不同客户端之间的行为。REST框架提供了许多不同的版本控制方案。

2. 版本控制由传入的客户端请求确定,可以基于请求URL,也可以基于请求标头。

3. 有许多有效的方法来处理版本控制。非版本化系统也是合适的,特别是如果您正在为具有多个客户端的长期系统进行工程设计。

02-版本控制方案

详解:https://www.django-rest-framework.org/api-guide/versioning/

03-版本控制的使用

3.1 全局配置

这里我们以 URLPathVersioning 为例,还是在项目的settings.py中REST_FRAMEWORK配置项下配置:

REST_FRAMEWORK = {
    ...
    # 去url路径里面获取版本
    'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
    'DEFAULT_VERSION': 'v1',              # 默认的版本
    'ALLOWED_VERSIONS': ['v1', 'v2'],  # 允许的版本
    'VERSION_PARAM': 'version',  # 版本的参数名与URL conf中一致
}

urls.py

urlpatterns = [
    ...
    url(r'^(?P<version>[v1|v2]+)/publishers/$', views.PublisherViewSet.as_view({'get': 'list', 'post': 'create'})),
    url(r'^(?P<version>[v1|v2]+)/publishers/(?P<pk>\d+)/$', views.PublisherViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),

]

我们可以在视图中自定义具体的行为,下面以不同的版本返回不同的序列化类为例:

class PublisherViewSet(ModelViewSet):

    queryset = models.Publisher.objects.all()

    def get_serializer_class(self):
        """不同的版本使用不同的序列化类"""
        # request.version:获取版本
        if self.request.version == 'v1':
            return PublisherModelSerializerVersion1
        else:
            return PublisherModelSerializer

3.2 局部配置

注意,通常我们是不会单独给某个视图设置版本控制的,如果你确实需要给单独的视图设置版本控制,你可以在视图中设置versioning_class属性,如下:

class PublisherViewSet(ModelViewSet):

    ...
    versioning_class = URLPathVersioning

04-反向解析url

# api/urls.py

from django.urls import path,re_path
from .views import UserView

urlpatterns = [
    re_path('(?P<version>[v1|v2]+)/users/', UserView.as_view(),name = 'api_user'),
]
# api/views.py

from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from rest_framework.request import Request

class UserView(APIView):

    def get(self,request,*args,**kwargs):
        #获取版本
        print(request.version)
        #获取处理版本的对象
        print(request.versioning_scheme)
        #获取浏览器访问的url,reverse反向解析
        #需要两个参数:viewname就是url中的别名,request=request是url中要传入的参数
        #(?P<version>[v1|v2]+)/users/,这里本来需要传version的参数,但是version包含在request里面(源码里面可以看到),所有只需要request=request就可以
        url_path = request.versioning_scheme.reverse(viewname='api_user',request=request)
        print(url_path)
        # self.dispatch
        return HttpResponse('用户列表')

05-versioning_class

06-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 = kwargs.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):
        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
        )

url配置案例:

6.1 determine_version

里面有个is_allowed_version,点进去可以看到一些基本参数 (继承BaseVersioning基类)

class BaseVersioning(object):
    #默认的版本
    default_version = api_settings.DEFAULT_VERSION
    #允许的版本
    allowed_versions = api_settings.ALLOWED_VERSIONS
    #默认参数(是version,比如你可以自定义为v)
    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
        return ((version is not None and version == self.default_version) or
                (version in self.allowed_versions))

猜你喜欢

转载自www.cnblogs.com/pgxpython/p/10292076.html