1. 程式人生 > >Django REST Framework之版本控制

Django REST Framework之版本控制

何謂版本控制?

為什麼需要版本控制?

一個專案在升級迭代的時候,不會立馬拋棄舊的版本,甚至會出現多個版本共存同時維護的情況,因此需要版本控制。

版本控制做了什麼?

版本控制做的事情很簡單,在前後端分離的情況下,只是對請求做判斷,判斷這是哪個版本的請求,然後將版本資訊封裝入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變數來獲取合法的版本號。

&n