API 版本控制允许我们在不同的客户端之间更改行为(同一个接口的不同版本会返回不同的数据)。 DRF提供了许多不同的版本控制方案。

可能会有一些客户端因为某些原因不再维护了,但是我们后端的接口还要不断的更新迭代,这个时候通过版本控制返回不同的内容就是一种不错的解决方案。

DRF提供的版本控制方案

DRF提供了五种版本控制方案,如下图:

版本控制系统的使用

全局配置

这里我们以 URLPathVersioning 为例,还是在项目的settings.py中REST_FRAMEWORK配置项下配置:

REST_FRAMEWORK = {...'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning','DEFAULT_VERSION': 'v1',           # 默认的版本'ALLOWED_VERSIONS': ['v1', 'v2'],  # 有效的版本'VERSION_PARAM': 'version',        # 版本的参数名与URL conf中一致
}

局部配置

注意,通常我们是不会单独给某个视图设置版本控制的,如果你确实需要给单独的视图设置版本控制,你可以在视图中设置versioning_class属性,如下:

class PublisherViewSet(ModelViewSet):...versioning_class = URLPathVersioning

urls.py

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'})),]

我们在视图中可以通过访问 request.version 来获取当前请求的具体版本,然后根据不同的版本来返回不同的内容:

我们可以在视图中自定义具体的行为,下面以不同的版本返回不同的序列化类为例

class PublisherViewSet(ModelViewSet):def get_serializer_class(self):"""不同的版本使用不同的序列化类"""if self.request.version == 'v1':return PublisherModelSerializerVersion1else:return PublisherModelSerializerqueryset = models.Publisher.objects.all()

REST framework 提供的默认版本的源码

############################## versioning.py #############################
# coding: utf-8
from __future__ import unicode_literalsimport refrom django.utils.translation import ugettext_lazy as _from rest_framework import exceptions
from rest_framework.compat import unicode_http_header
from rest_framework.reverse import _reverse
from rest_framework.settings import api_settings
from rest_framework.templatetags.rest_framework import replace_query_param
from rest_framework.utils.mediatypes import _MediaType# 基础类,其他类要继承此类
class BaseVersioning(object):default_version = api_settings.DEFAULT_VERSION           # 配置文件中获取相应信息allowed_versions = api_settings.ALLOWED_VERSIONSversion_param = api_settings.VERSION_PARAMdef 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 Truereturn ((version is not None and version == self.default_version) or(version in self.allowed_versions))# 版本信息在头部
class AcceptHeaderVersioning(BaseVersioning):"""GET /something/ HTTP/1.1Host: example.comAccept: application/json; version=1.0"""invalid_version_message = _('Invalid version in "Accept" header.')def determine_version(self, request, *args, **kwargs):media_type = _MediaType(request.accepted_media_type)version = media_type.params.get(self.version_param, self.default_version)version = unicode_http_header(version)if not self.is_allowed_version(version):raise exceptions.NotAcceptable(self.invalid_version_message)return version# We don't need to implement `reverse`, as the versioning is based# on the `Accept` header, not on the request URL.# 版本信息在url中
class URLPathVersioning(BaseVersioning):"""To the client this is the same style as `NamespaceVersioning`.The difference is in the backend - this implementation usesDjango'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.1Host: example.comAccept: 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 version is None:version = self.default_versionif not self.is_allowed_version(version):raise exceptions.NotFound(self.invalid_version_message)return versiondef reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):if request.version is not None:kwargs = {} if (kwargs is None) else kwargskwargs[self.version_param] = request.versionreturn super(URLPathVersioning, self).reverse(viewname, args, kwargs, request, format, **extra)class NamespaceVersioning(BaseVersioning):"""To the client this is the same style as `URLPathVersioning`.The difference is in the backend - this implementation usesDjango's URL namespaces to determine the version.An example URL conf that is namespaced into two separate versions# users/urls.pyurlpatterns = [url(r'^/users/$', users_list, name='users-list'),url(r'^/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail')]# urls.pyurlpatterns = [url(r'^v1/', include('users.urls', namespace='v1')),url(r'^v2/', include('users.urls', namespace='v2'))]GET /1.0/something/ HTTP/1.1Host: example.comAccept: application/json"""invalid_version_message = _('Invalid version in URL path. Does not match any version namespace.')def determine_version(self, request, *args, **kwargs):resolver_match = getattr(request, 'resolver_match', None)if resolver_match is None or not resolver_match.namespace:return self.default_version# Allow for possibly nested namespaces.possible_versions = resolver_match.namespace.split(':')for version in possible_versions:if self.is_allowed_version(version):return versionraise exceptions.NotFound(self.invalid_version_message)def reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):if request.version is not None:viewname = self.get_versioned_viewname(viewname, request)return super(NamespaceVersioning, self).reverse(viewname, args, kwargs, request, format, **extra)def get_versioned_viewname(self, viewname, request):return request.version + ':' + viewnameclass HostNameVersioning(BaseVersioning):"""GET /something/ HTTP/1.1Host: v1.example.comAccept: application/json"""hostname_regex = re.compile(r'^([a-zA-Z0-9]+)\.[a-zA-Z0-9]+\.[a-zA-Z0-9]+$')invalid_version_message = _('Invalid version in hostname.')def determine_version(self, request, *args, **kwargs):hostname, separator, port = request.get_host().partition(':')match = self.hostname_regex.match(hostname)if not match:return self.default_versionversion = match.group(1)if not self.is_allowed_version(version):raise exceptions.NotFound(self.invalid_version_message)return version# We don't need to implement `reverse`, as the hostname will already be# preserved as part of the REST framework `reverse` implementation.# 通过url参数区分
class QueryParameterVersioning(BaseVersioning):"""GET /something/?version=0.1 HTTP/1.1Host: example.comAccept: application/json"""invalid_version_message = _('Invalid version in query parameter.')def determine_version(self, request, *args, **kwargs):version = request.query_params.get(self.version_param, self.default_version)if not self.is_allowed_version(version):raise exceptions.NotFound(self.invalid_version_message)return versiondef reverse(self, viewname, args=None, kwargs=None, request=None, format=None, **extra):url = super(QueryParameterVersioning, self).reverse(viewname, args, kwargs, request, format, **extra)if request.version is not None:return replace_query_param(url, self.version_param, request.version)return url

Django REST framework 版本相关推荐

  1. Django REST framework 1

    Django REST framework Django REST framework官方文档:点击  中文文档:点击 安装djangorestframework:pip3 install djang ...

  2. DRF (Django REST framework) 框架介绍

    Web应用模式 在开发Web应用中,有两种应用模式: 前后端不分离 前后端分离 1 前后端不分离 在前后端不分离的应用模式中,前端页面看到的效果都是由后端控制,由后端渲染页面或重定向,也就是后端需要控 ...

  3. Django Rest Framework(一)

    一.什么是RESTful REST与技术无关,代表一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为"表征状态转移". R ...

  4. Django Rest framework Request

    [Django Rest framework文档翻译]-Request REST framework的Request类扩展自标准的HttpRequest,增加了REST framework灵活的请求解 ...

  5. Django REST framework API开发

    REST 介绍 RESTful API 设计 实现API的两种方式 FBV 视图函数 urlpatterns = [url(r'^user/$', views.user),url(r'^user/ad ...

  6. python全栈生鲜电商_Vue+Django REST framework 打造生鲜电商项目(学习笔记一)

    1.环境搭建 所需软件的版本: 1)pycharm(使用professional版本) 2)mysql.navicat 安装好的mysql后需要给root权限,不然只能通过localhost访问本地的 ...

  7. [转]Django REST framework 简介与中文教程

    Django REST framework 简介与中文教程 简介 在序列化与反序列化时,虽然操作的数据不尽相同,但是执行的过程却是相似的,也就是说这部分代码是可以复用简化编写的. 在开发REST AP ...

  8. Django Rest framework (看完直接上手用)

    Restframework DjangoRestframework 主要使用 APIView 类,其 APIView 实质是对 View 进行继承加工了更多功能 请求进来 APIView() 首先执行 ...

  9. Django REST framework学习笔记

    文章目录 1. API接口开发 1.1 获取数据的接口 1.2 添加数据的接口 1.3 更新数据的接口 1.4 删除数据的接口 2. API字段的定制 2.1 别名字段 2.2 字段格式化 2.3 字 ...

最新文章

  1. 目标检测模型从训练到部署!
  2. 南洋理工75页最新「深度学习对话系统」大综述论文,最全面概述深度学习对话技术进展...
  3. 普通程序员如何逆袭,达到财富自由?
  4. Twisted入门教程(12)
  5. 微信小程序开发系列一:微信小程序的申请和开发环境的搭建
  6. LUA学习之路--初识LUA
  7. 推荐:李沐开源新作,一起来《动手学深度学习》
  8. -webkit-scrollbar
  9. [js高手之路]从零开始打造一个javascript开源框架gdom与插件开发免费视频教程连载中...
  10. C#中类与结构体的区别
  11. 即将举行的网络研讨会:调试生产中Java的5种最佳实践
  12. javascript学习之支持正则表达式的String对象的方法的使用 search match replace split
  13. tableau制作中国地图(全)
  14. 智能人物画像综合分析系统——Day16
  15. 第一次在csdn写博客!
  16. ue4 c++绘线 DrawLine
  17. 如何用proe/croe将三维文件转为二维CAD轴侧图
  18. 一文搞懂「微信支付 Api-v3」接口规则所有知识点
  19. Python判断某个列表是否是另一个列表的子列表
  20. [PTA]练习5-3 数字金字塔

热门文章

  1. 程序员赚钱致富的6种方法
  2. Facebook与Google的互联网霸主争夺战
  3. springCloud - 第5篇 - 断路器 Hystrix ( Feign 、Ribbon )
  4. JSch:Java Secure Channel -- java 代码实现 ssh 远程操作
  5. MyEclipse 皮肤、主题、背景色
  6. 在Asp.net core返回PushStream
  7. 【MySQL】PREPARE 的应用
  8. sql语句优化之not in
  9. SAP Study Notes: BW Queriy-Variables(变量)
  10. [Silverlight]使用PagedCollectionView配合复选框实现动态筛选的解决方案