Django REST framework中的版本控制

1.REST framework版本控制的流程分析

1.1 determine_version方法的執行流程

首先,請求到達REST framework的CBV,執行CBV中的dispatch方法再次封裝完成request後,執行initial方法.django

REST framework中的版本控制就是在initial函數中調用determine_version方法完成的api

來看看源碼瀏覽器

initial方法的源碼:app

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)

determine_version方法的源碼函數

def determine_version(self, request, *args, **kwargs):

    if self.versioning_class is None:    # 若是versioning_class爲空則返回一個None的元組
        return (None, None) 
    scheme = self.versioning_class()
    return (scheme.determine_version(request, *args, **kwargs), scheme)

determine_version方法中的versioning_class方法又是從哪裏來的呢測試

1.2 versioning_class的由來

在UserView視圖函數中沒有定義versioning_class,那就要到UserView的父類APIView中去找url

在APIView類中定義了versioning_class的信息spa

class APIView(View):

    renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
    parser_classes = api_settings.DEFAULT_PARSER_CLASSES
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
    throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
    metadata_class = api_settings.DEFAULT_METADATA_CLASS
    versioning_class = api_settings.DEFAULT_VERSIONING_CLASS

在視圖函數中打印versioning_class版本控制

None

能夠看到默認設置的versioning_class的值是None,這說明咱們能夠在視圖函數中爲versioning_class設置一個值rest

detemine_version函數的源碼中,能夠看到versioning_class後面加了一個括號,因此versioning_class是一個函數或一個類

若是versioning_class是一個函數,那麼執行versioning_class後會有一個返回值
若是versioning_class是一個類,那麼versioning_class加括號就實例化一個類

從rest_framework中導入versioning模塊

from rest_framework import versioning

而後進入versioning模塊,能夠看到這個versioning中定義了6個類

這6個類是BaseVersioning,AcceptHeaderVersioning,URLPathVersioning,NamespaceVersioning,HostNameVersioning,QueryParameterVersioning

並且還能夠看到BaseVersioning類是其他5個類的父類.

而且這其他的5個類,每一個類中都有一個determine_version方法

在項目的視圖函數中導入其中任意一個類,打印versioning_class

from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from django.views import View
from rest_framework.versioning import QueryParameterVersioning

class UsersView(APIView):
    versioning_class=QueryParameterVersioning

    def get(self,request,*args,**kwargs):
        print(self.versioning_class)        #打印versioning_class

        return HttpResponse("aaaa")

打印結果

<class 'rest_framework.versioning.QueryParameterVersioning'>

因此versioning_class是一個類,而且versioning_class類中有一個determine_version方法

1.3 REST framework版本控制的流程總結

initial方法中,執行完determine_version後的返回值被賦值給version, scheme這兩個變量

def initial(self, request, *args, **kwargs):

    self.format_kwarg = self.get_format_suffix(**kwargs)

    neg = self.perform_content_negotiation(request)
    request.accepted_renderer, request.accepted_media_type = neg

    version, scheme = self.determine_version(request, *args, **kwargs)
    request.version, request.versioning_scheme = version, scheme

    self.perform_authentication(request)
    self.check_permissions(request)
    self.check_throttles(request)

這兩個變量又把determine_version方法的返回值賦值給request.version, request.versioning_scheme這兩個變量

在視圖函數中打印這兩個變量

from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from rest_framework.versioning import QueryParameterVersioning

class UsersView(APIView):
    versioning_class=QueryParameterVersioning

    def get(self,request,*args,**kwargs):
        print(self.versioning_class)
        print("request.version:",request.version)
        print("request.versioning_scheme:",request.versioning_scheme)

        return HttpResponse("aaaa")

打印結果

<class 'rest_framework.versioning.QueryParameterVersioning'>
request.version: None
request.versioning_scheme: <rest_framework.versioning.QueryParameterVersioning object at 0x00000000057722B0>

2. REST framework獲取版本的方法

在上面的流程分析中,versioning模塊中定義了6個類

這6個類是BaseVersioning,AcceptHeaderVersioning,URLPathVersioning,NamespaceVersioning,HostNameVersioning,QueryParameterVersioning

BaseVersioning類是其他5個類的父類,REST framework獲取版本調用的就是這5個類

2.1 QueryParameterVersioning:基於url的get傳參方式

在settings.py文件的INSTALLED_APPS配置項中引入rest-framework

INSTALLED_APPS = [
    ...
    'rest_framework',
]

配置路由表

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^users/$',views.UsersView.as_view()),
]

視圖函數配置獲取版本方式爲QueryParameterVersioning

from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from rest_framework.versioning import QueryParameterVersioning

class UsersView(APIView):
    versioning_class=QueryParameterVersioning

    def get(self,request,*args,**kwargs):
        # print("request:",request.__dict__)
        print("request.version:",request.version)       # 打印版本
        # print(request.version.scheme)
        # print(request.versioning_scheme.reverse("test1",request=request))
        # print(request.versioning_scheme.reverse(viewname="test1",request=request))

        return HttpResponse("aaaa")

在瀏覽器中輸入http://127.0.0.1:8000/users/?version=v1地址,服務端打印結果

request.version: v1

再把瀏覽器中的url地址更換爲http://127.0.0.1:8000/users/?version=v5,刷新瀏覽器,服務端打印結果

request.version: v5

2.2 URLPathVersioning:基於url的正則方式

配置url路由信息

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^(?P<version>\w+)/users/$',views.UsersView.as_view()),
]

視圖函數配置獲取版本方式爲URLPathVersioning

from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from rest_framework.versioning import URLPathVersioning

class UsersView(APIView):
    versioning_class=URLPathVersioning

    def get(self,request,*args,**kwargs):

        print("request.version:",request.version)   # 打印版本

        return HttpResponse("aaaa")

在瀏覽器中輸入http://127.0.0.1:8000/v1/users/地址,服務端打印結果

request.version: v1

再把瀏覽器中的url地址更換爲http://127.0.0.1:8000/v10/users/,刷新瀏覽器,服務端打印結果

request.version: v10

2.3 AcceptHeaderVersioning:基於accept請求頭方式獲取版本信息

在settings.py文件中添加以下配置

REST_FRAMEWORK = {
    'VERSION_PARAM': "version",     # 版本的參數,在url中能夠體現
    'DEFAULT_VERSION': 'V1',        # 默認的版本
    'ALLOWED_VERSIONS': ['v1', 'v2','v3']  # 容許的版本
}

urls.py設定爲

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^users/$',views.UsersView.as_view()),
]

視圖函數定義

from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from rest_framework.versioning import AcceptHeaderVersioning

class UsersView(APIView):
    versioning_class=AcceptHeaderVersioning

    def get(self,request,*args,**kwargs):

        print("request.version:",request.version)   # 獲取版本信息

        return HttpResponse("aaaa")

用瀏覽器打開http://127.0.0.1:8000/users/的url地址

request.version: V1

因爲在settings.py文件中已經設定了默認的版本是v1,因此在服務端後臺獲取到的版本是v1

把settings.py中定義的默認版本更改變v2或者v3,再次刷新瀏覽器,後臺打印的版本信息又會跟着改變

request.version: V2

2.4 NamespaceVersioning:基於django路由系統的namespace

urls.py設定爲

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^v1/users/',([url(r'test/',views.UsersView.as_view(),name='test1')],None,'v1')),
    url(r'^v2/users/',([url(r'test/',views.UsersView.as_view(),name='test2')],None,'v2')),
]

視圖函數定義

from django.shortcuts import render,HttpResponse
from rest_framework.views import APIView
from rest_framework.versioning import NamespaceVersioning

class UsersView(APIView):
    versioning_class=NamespaceVersioning

    def get(self,request,*args,**kwargs):
        print("request.version:",request.version)   # 獲取版本信息

        return HttpResponse("aaaa")

用瀏覽器打開http://127.0.0.1:8000/v1/users/test/的url地址

request.version: V1

把url的地址更換爲http://127.0.0.1:8000/v2/users/test/,刷新瀏覽器,後臺打印信息以下

request.version: V2

2.5 HostNameVersioning因爲要更換電腦的主機名稱,因此這裏再也不進行測試

3. versioning_class的全局配置

在視圖函數中定義versioning_class,只能做用於單個類,

若是想整個項目都使用同一種方法來進行版本控制,就能夠在settings.py文件中定義全局的versioning_class

settings.py中配置默認的versioning_classURLPathVersioning

REST_FRAMEWORK={
    'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
}

在前面查看到BaseVersioning的源碼時,能夠看到還有幾個參數能夠在settings.py文件中定義的

class BaseVersioning(object):
    default_version = api_settings.DEFAULT_VERSION
    allowed_versions = api_settings.ALLOWED_VERSIONS
    version_param = api_settings.VERSION_PARAM

再來看看這幾個參數配置項的做用

REST_FRAMEWORK={            # 默認使用URLPathVersioning類來獲取版本信息
    'DEFAULT_VERSIONING_CLASS':'rest_framework.versioning.URLPathVersioning',
    'VERSION_PARAM':"version",          # 版本的參數,在url中能夠體現
    'DEFAULT_VERSION':'V1',             # 默認的版本
    'ALLOWED_VERSIONS':['v1','v2']      # 容許的版本
}

修改urls.py文件,使url能夠匹配任意長度的字符url

from django.conf.urls import url
from django.contrib import admin
from app01 import views

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^(?P<version>\w+)/users/$',views.UsersView.as_view()),
]

在瀏覽器中分別輸入http://127.0.0.1:8000/v1/users/http://127.0.0.1:8000/v2/users/

均可以獲取到正確的響應信息

再在瀏覽器中輸入http://127.0.0.1:8000/v3/users/時,瀏覽器中出現了報錯

從這裏能夠知道,在settings.py文件中設定的url中容許的版本只能是v1或v2,在瀏覽器中輸入的版本是v3,因此就會出現錯誤了

相關文章
相關標籤/搜索