Django REST Framework之版本控制

何谓版本控制?

为何须要版本控制?

一个项目在升级迭代的时候,不会立马抛弃旧的版本,甚至会出现多个版本共存同时维护的状况,所以须要版本控制。前端

版本控制作了什么?

版本控制作的事情很简单,在先后端分离的状况下,只是对请求作判断,判断这是哪一个版本的请求,而后将版本信息封装入request对象中。python

自定义版本控制类

1.settings.py配置

REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS": "utils.version.MyVersion", # 默认使用的版本控制类
}

2.编写自定义版本控制类(根据请求参数)

class MyVersion(object):
    def determine_version(self, request, *args, **kwargs):
        "版本号携带在请求"
        version = request.query_params.get("version", "v1") # 请求参数中查找有无version字段,若是没有默认是v1
        return version # 将版本返回

3.视图中获取

request.version     # 版本号
request.versioning_scheme  # 版本控制类的实例

使用DRF的版本控制类

Django REST Framework为咱们提供了5个版本控制类,分别是五种不一样的判断方式,基本能知足开发需求。正则表达式

1.AcceptHeaderVersioning 将版本信息放在请求头
2.URLPathVersioning      将版本信息放在URL中,
3.NamespaceVersioning    将版本信息放在URL中,不一样之处在于Django路由的处理方式,使用命名空间
4.HostNameVersioning     将版本信息放在域名的最低一层
5.QueryParameterVersioning 降版本信息放在请求参数中

直接在settings中配置就能够用:django

REST_FRAMEWORK = {
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.QueryParameterVersioning",
    "DEFAULT_VERSION": "v1.0.0",    # 默认的版本,当没法从请求中获取版本信息的时候,默认按照此版本执行
    "ALLOWED_VERSIONS": "v1.0.0, v1.1.0, v2.0.0", # 容许的版本
    "VERSION_PARAM": "ver", # 获取版本的参数。
}

源码分析

1.为何用 “versioning_class”属性变量,它有什么用?

仍是要从APIView类中的dispatch开始提及,跟以前讲的认证等功能接口同样,首先要完成对django原生request的封装,而后一样是在initial方法中实现版本控制。在initial方法中能够看到,是经过调用determine_version方法实现版本控制。后端

在determine_version方法中就能够看到versioning_class属性变量,再跳转到version_class定义的地方。能够看到,它的初始值是经过配置文件设置的,而这种方式,主要是来实现全局配置的(下文会说明全局配置)。经过全局配置和“scheme = self.versioning_class()”代码能够看出,这段代码是在作对象的实例化,因此version_class属性变量存储的是一个对象,具体是哪一个对象,全局配置方法中告诉了咱们。使用的就是基本代码结构中的“URLPathVersioning”对象。而这个类就是实现版本控制核心代码的类。当咱们不使用全局配置时,那么咱们就必须在自定义的视图类中给version_class属性变量赋值,即:version_class = URLPathVersioning。前后端分离

2.在urls.py中,版本的正在为何要写成“(?P<version>[v1|v2]+)”的形式?为何正则中的变量是version?

 

跳转到URLPathVersioning类定义的地方能够看到,这种样式的正则“(?P<version>[v1|v2]+)”url的编写方式是在源码中已经规定好的,因此咱们要使用源码中提供的接口。源码分析

3.在settings配置文件中,为何会使用“DEFAULT_VERSION”,"ALLOWED_VERSION","VERSION_PARM"这三个key值来作版本配置?

由URLPathVersioning类的定义能够看出,它的父类是BaseVersioning类,先看一下这个类的具体定义。最开始定义的三个属性变量“default_version”,“allowed_versions”,“version_param”的值,也都是经过settings配置文件赋值的,而这三个值不就是问题中提到的三个key值吗?url

再看determine_version方法,它会抛出异常,因此它的子类(URLPathVersioning类)必须重写这个方法。而下面的is_allowed_version方法,就是用来判断前端请求的版本号是不是配置文件中所配置的版本之一,这就用到了allowed_versions属性变量。若是没有配置allowed_versions变量(也就是说没有设置版本控制的功能,那天然是容许经过的,返回True),若是作了版本控制,那么就要判断当前获取的版本号(version变量)是否在配置的版本中,这里就会用到default_version变量和allowed_versions变量。spa

reverse方法是用于URL反射用的,这里就很少谈了。3d

再回到URLPathVersioning类中,能够看到determine_version方法。在这个方法中能够看到,经过kwargs.get()来获取前端请求时的版本号,这就要用到version_param和default_version变量。值得注意的是在对version_param变量配置时,的字符串必须跟在urls.py中的正则表达式中的变量保存一致(这里用的是“version”字符串)。当version变量为None时,就使用默认的版本。以后再调用is_allowed_version方法,完成对当前版本的是否合法的判断。最后返回合法的版本号。

再回到APIView类中的initial方法中,能够看到它也调用了本身的determine_version方法。在determine_version方法中,能够看到上文提到过的versioning_class属性变量,它保存了URLPathVersion类对象。再看determine_version方法的返回值,返回的是一个元组。元组第一个元素正是上文提到过的URLPathVersion类中的determine_version方法,而这个方法返回的也正是合法的版本号。元组第二个元素是就是这个版本类对象。

再来到initial方法中,determine_version方法返回的元组的值,分别赋值到了request.version和request_versioning_scheme变量中。所以,在代码基本结构中,经过request.version变量来获取合法的版本号。