對接口進行版本控制只是一種殺死已部署客戶端的「禮貌」方式。 - 羅伊菲爾丁。 1. API版本控制容許您更改不一樣客戶端之間的行爲。REST框架提供了許多不一樣的版本控制方案。 2. 版本控制由傳入的客戶端請求肯定,能夠基於請求URL,也能夠基於請求標頭。 3. 有許多有效的方法來處理版本控制。非版本化系統也是合適的,特別是若是您正在爲具備多個客戶端的長期系統進行工程設計。
詳解:https://www.django-rest-framework.org/api-guide/versioning/django
這裏咱們以 URLPathVersioning 爲例,仍是在項目的settings.py中REST_FRAMEWORK配置項下配置:json
REST_FRAMEWORK = { ... # 去url路徑裏面獲取版本 'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning', 'DEFAULT_VERSION': 'v1', # 默認的版本 'ALLOWED_VERSIONS': ['v1', 'v2'], # 容許的版本 'VERSION_PARAM': 'version', # 版本的參數名與URL conf中一致 }
urls.pyapi
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
注意,一般咱們是不會單獨給某個視圖設置版本控制的,若是你確實須要給單獨的視圖設置版本控制,你能夠在視圖中設置versioning_class屬性,以下:app
class PublisherViewSet(ModelViewSet): ... versioning_class = URLPathVersioning
# 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('用戶列表')
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配置案例:框架
裏面有個is_allowed_version,點進去能夠看到一些基本參數 (繼承BaseVersioning基類)ide
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))