何謂版本控制?
為什么需要版本控制?
一個項目在升級迭代的時候,不會立馬拋棄舊的版本,甚至會出現多個版本共存同時維護的情況,因此需要版本控制。
版本控制做了什么?
版本控制做的事情很簡單,在前后端分離的情況下,只是對請求做判斷,判斷這是哪個版本的請求,然后將版本信息封裝入request對象中。
自定義版本控制類
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中配置就可以用:
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值嗎?
再看determine_version方法,它會拋出異常,所以它的子類(URLPathVersioning類)必須重寫這個方法。而下面的is_allowed_version方法,就是用來判斷前端請求的版本號是否是配置文件中所配置的版本之一,這就用到了allowed_versions屬性變量。如果沒有配置allowed_versions變量(也就是說沒有設置版本控制的功能,那自然是允許通過的,返回True),如果做了版本控制,那么就要判斷當前獲取的版本號(version變量)是否在配置的版本中,這里就會用到default_version變量和allowed_versions變量。
reverse方法是用於URL反射用的,這里就不多談了。
再回到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變量來獲取合法的版本號。