一、解析器(parser)
解析器在reqest.data取值的时候才执行。
对请求的数据进行解析:是针对请求体进行解析的。表示服务器可以解析的数据格式的种类。
from rest_framework.parsers import JSONParser, FormParser, MultiPartParser, FileUploadParser
"""
默认得是 JSONParser FormParser MultiPartParser
"""
class BookView(APIView):
# authentication_classes = [TokenAuth, ]
parser_classes = [FormParser, JSONParser]
def get(self, request):....
1、django的request类源码解析
(1)django中发送请求对比
#如果是urlencoding格式发送的数据,在POST里面有值
Content-Type: application/url-encoding.....
request.body
request.POST
#如果是发送的json格式数据,在POST里面是没有值的,在body里面有值,可通过decode,然后loads取值
Content-Type: application/json.....
request.body
request.POST
(2)关于decode和encode
浏览器发送过来是字节需要先解码 ---> decode 如:s=‘中文‘
如果是在utf8的文件中,该字符串就是utf8编码,如果是在gb2312的文件中,则其编码为gb2312。这种情况下,要进行编码转换,都需要先用 decode方法将其转换成unicode编码,再使用encode方法将其转换成其他编码。通常,在没有指定特定的编码方式时,都是使用的系统默认编码创建的代码文件。 如下:
s.decode(‘utf-8‘).encode(‘utf-8‘)
decode():是解码 --->把字节变成字符串 encode()是编码---->把字符串变成字节
(3)查看django中WSGIRequest解析方法
# 1:导入django的类
from django.core.handlers.wsgi import WSGIRequest
# 2:
class WSGIRequest(http.HttpRequest):
def _get_post(self):
if not hasattr(self, ‘_post‘):
self._load_post_and_files()
return self._post
# 3:self._load_post_and_files()从这里找到django解析的方法
def _load_post_and_files(self):
"""Populate self._post and self._files if the content-type is a form type"""
if self.method != ‘POST‘:
self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
return
if self._read_started and not hasattr(self, ‘_body‘):
self._mark_post_parse_error()
return
if self.content_type == ‘multipart/form-data‘:
if hasattr(self, ‘_body‘):
# Use already read data
data = BytesIO(self._body)
else:
data = self
try:
self._post, self._files = self.parse_file_upload(self.META, data)
except MultiPartParserError:
# An error occurred while parsing POST data. Since when
# formatting the error the request handler might access
# self.POST, set self._post and self._file to prevent
# attempts to parse POST data again.
# Mark that an error occurred. This allows self.__repr__ to
# be explicit about it instead of simply representing an
# empty POST
self._mark_post_parse_error()
raise
elif self.content_type == ‘application/x-www-form-urlencoded‘:
self._post, self._files = QueryDict(self.body, encoding=self._encoding), MultiValueDict()
else:
self._post, self._files = QueryDict(encoding=self._encoding), MultiValueDict()
从if self.content_type == ‘multipart/form-data‘:和 self.content_type == ‘application/x-www-form-urlencoded‘: 可以知道django只解析urlencoded‘和form-data这两种类型。
因此在django中传json数据默认是urlencoded解析到request中:body取到json数据时,取到的数据时字节,需要先decode解码,将字节变成字符串。
request.body.decode("utf8")
json.loads(request.body.decode("utf8"))
为了传输json数据每次都要decode\loads,比较麻烦因此才有了解析器解决这个问题。
2、rest-framework的request类源码解析
在rest-framework中 是以利用Request类进行数据解析。
1:找到apiview
class APIView(View):
# The following policies may be set at either globally, or per-view.
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES # 解析器
2:找api_settings没有定义找默认
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
3:.
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)
4:DEFAULTS
DEFAULTS = {
# Base API policies # 自带的解析器
‘DEFAULT_PARSER_CLASSES‘: (
‘rest_framework.parsers.JSONParser‘, # 仅处理请求头content-type为application/json的请求体
‘rest_framework.parsers.FormParser‘, # 仅处理请求头content-type为application/x-www-form-urlencoded 的请求体
‘rest_framework.parsers.MultiPartParser‘ # 仅处理请求头content-type为multipart/form-data的请求体
),
注意除了上面三种之外还有一个专门处理文件上传:
from rest_framework.parsers import FileUploadParser
3、局部视图parser
from rest_framework.parsers import JSONParser,FormParser
class PublishViewSet(generics.ListCreateAPIView):
parser_classes = [FormParser,JSONParser]
queryset = Publish.objects.all()
serializer_class = PublshSerializers
def post(self, request, *args, **kwargs):
print("request.data",request.data)
return self.create(request, *args, **kwargs)
4、全局视图parser
REST_FRAMEWORK={
"DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",],
"DEFAULT_PERMISSION_CLASSES":["app01.service.permissions.SVIPPermission",],
"DEFAULT_THROTTLE_CLASSES":["app01.service.throttles.VisitThrottle",],
"DEFAULT_THROTTLE_RATES":{
"visit_rate":"5/m",
},
"DEFAULT_PARSER_CLASSES":['rest_framework.parsers.FormParser',]
}
二、url路由控制
因为我们使用的是视图集而不是视图,我们可以通过简单地将视图集注册到router类来为我们的API自动生成URL conf。
同样,如果我们需要对API URL有更多的控制,我们可以直接使用常规的基于类的视图,并显式地编写URL conf。
最后,我们将默认登录和注销视图包含在可浏览API中。这是可选的,但如果您的API需要身份验证,并且希望使用可浏览的API,那么这是有用的。
1、全自动路由示例
from django.contrib import admin
from django.urls import path, re_path, include
from rest_framework import routers
from app01 import views
routers = routers.DefaultRouter() # 实例化
routers.register("authors", views.AuthorViewSet) # 注册某一个视图
urlpatterns = [
path('admin/', admin.site.urls),
...
# as_view参数指定什么请求走什么方法
# re_path(r'^authors/$', views.AuthorViewSet.as_view({"get": "list", "post": "create"}), name="author_list"),
# re_path(r'^authors/(?P<pk>\d+)/$', views.AuthorViewSet.as_view({
# 'get': 'retrieve',
# 'put': 'update',
# 'patch': 'partial_update',
# 'delete': 'destroy'
# }), name="author_detail"),
# 改写如下:
re_path(r"", include(routers.urls)),
re_path(r'^login/$', views.LoginView.as_view(), name="login"),
]
视图的内容不需要任何变动即生效:
2、DRF路由组件使用
路由传参写的特别多,但是框架将这些也已经封装好了。
修改DRFDemo/urls.py文件如下所示:
from django.urls import path, include from .views import BookView, BookEditView, BookModelViewSet from rest_framework.routers import DefaultRouter router = DefaultRouter() # 路由实例化 # 第一个参数是路由匹配规则,这里的路由是分发下来的,因此可以不做设置;第二个参数是视图 router.register(r"", BookModelViewSet) urlpatterns = [ # path('list', BookView.as_view()), # 查看所有的图书 # 注意url中参数命名方式,2.0之前的写法:'retrieve/(?P<id>\d+)' # 2.0之后的写法:<>内声明类型,冒号后面跟着关键字参数 # path('retrieve/<int:id>', BookEditView.as_view()) # 单条数据查看 # path('list', BookModelViewSet.as_view({"get": "list", "post": "create"})), # path('retrieve/<int:id>', BookModelViewSet.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})) ] urlpatterns += router.urls # router.urls是自动生成带参数的路由
但是需要自定制的时候还是需要我们自己用APIView写,当不需要那么多路由的时候,不要用这种路由注册,否则会对外暴露过多的接口,会存在风险。总之,一切按照业务需要去用。
三、分页组件(Pagination)
REST框架支持自定义分页风格,你可以修改每页显示数据集合的最大长度。
分页链接支持以下两种方式提供给用户:
- 分页链接是作为响应内容提供给用户
- 分页链接被包含在响应头中(Content-Range或者Link)
内建风格使用作为响应内容提供给用户。这种风格更容易被使用可浏览API的用户所接受。
如果使用通用视图或者视图集合。系统会自动帮你进行分页。
如果使用的是APIView,你就需要自己调用分页API,确保返回一个分页后的响应。可以将pagination_class设置为None关闭分页功能。
1、设置分页风格
可以通过设置DEFAULT_PAGINATION_CLASS和PAGE_SIZE,设置全局变量。
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 100
}
需要同时设置pagination class和page size。 也可以在单个视图中设置pagination_class属性,但一般你需要使用统一的分页风格。
2、修改分页风格
如果你需要修改分页风格 ,需要重写分页类,并设置你需要修改的属性。
from rest_framework.views import APIView
from rest_framework.response import Response
from app01.serializer import *
from .utils import *
#分页
from rest_framework.pagination import PageNumberPagination
class MyPageNumberPagination(PageNumberPagination):
# 默认每页显示的数据条数
page_size = 2
# 获取URL参数中设置的每页显示数据条数
page_size_query_param = 'size'
# 获取URL参数中传入的页码key
page_query_param = 'page'
# 最大支持的每页显示的数据条数(对这个进行限制127.0.0.1:8000/books/?page=1&size=100)
max_page_size = 3
class BookView(APIView):
# authentication_classes = [TokenAuth, ]
# parser_classes = [FormParser, JSONParser]
pagination_class = MyPageNumberPagination # 在视图中使用pagination_class属性调用该自定义类
def get(self, request):
book_list = Book.objects.all() # queryset
# 实例化分页对象,获取数据库中的分页数据
pnp = MyPageNumberPagination()
books_page = pnp.paginate_queryset(book_list, request, self)
# 序列化对象
bs = BookModelSerializers(books_page, many=True, context={"request": request}) # 序列化结果
# return Response(bs.data)
return bs.get_paginated_response(bs.data) # 生成分页和数据
def post(self, request):.....
(1)设置max_page_size是限制直接在页面访问时最大的数据条数显示
虽然总共有4条数据,页面访问get请求时?page=1&size=100但是依然只能拿到max_page_size限制拿到的3条。
3、根据页码分页——PageNumberPagination
这个分页样式接受请求查询参数中的一个数字页面号。
GET https://api.example.org/accounts/?page=4
响应对象:
HTTP 200 OK
{
"count": 1023
"next": "https://api.example.org/accounts/?page=5",
"previous": "https://api.example.org/accounts/?page=3",
"results": [
…
]
}
继承了APIView的视图,也可以设置pagination_class属性选择PageNumberPagination
class MyPageNumberPagination(PageNumberPagination):
# 默认每页显示的数据条数
page_size = 1
# 获取URL参数中设置的每页显示数据条数
page_size_query_param = 'size'
# 获取URL参数中传入的页码key
page_query_param = 'page'
# 最大支持的每页显示的数据条数(对这个进行限制127.0.0.1:8000/books/?page=1&size=100)
max_page_size = 3
更多配置属性:
- django_paginator_class 使用Django分页类。默认为django.core.paginator.Paginator,适用于大多数情况
- page_size 用来显示每页显示对象的数量,如果设置了就重写PAGE_SIZE设置。
- page_query_param 页面查询参数,一个字符串值,指示用于分页控件的查询参数的名称。
- page_size_query_param 该参数允许客户端根据每个请求设置页面大小。一般默认设置为None.
- max_page_size 只有设置了page_size_query_param参数,该参数才有意义,为客户端请求页面中能够显示的最大数量
- last_page_strings 用于存储使用page_query_param参数请求过的值列表或元组,默认为(‘last’,)
- template 用来在可浏览API中,渲染分页的模板(html)名字,可以重写分页样式,或者设置为None,禁用分页。默认为”rest_framework/pagination/numbers.html”。
4、根据位置和个数分页——LimitOffsetPagination
这种分页样式与查找多个数据库记录时使用的语法类似。客户端包括一个”limit”和一个 “offset”查询参数。该限制表示返回的条目的最大数量,并且与page_size大小相同。偏移量表示查询的起始位置,与完整的未分页项的集合有关。
GET https://api.example.org/accounts/?limit=100&offset=400
HTTP 200 OK
{
"count": 1023
"next": "https://api.example.org/accounts/?limit=100&offset=500",
"previous": "https://api.example.org/accounts/?limit=100&offset=300",
"results": [
…
]
}
这种也可以设置PAGE_SIZE,然后客户端就可以设置limit参数了。
继承了GenericAPIView的子类,可以通过设置pagination_class属性为LimitOffsetPagination使用
class MyLimitOffsetPagination(LimitOffsetPagination):
# 默认每页显示的数据条数
default_limit = 1
# URL中传入的显示数据条数的参数
limit_query_param = 'limit'
# URL中传入的数据位置的参数
offset_query_param = 'offset'
# 最大每页显得条数
max_limit = None
(重写LimitOffsetPagination类)配置:
- default_limit: 如果客户端没有提供,则默认使用与PAGE_SIZE值一样。
- limit_query_param:表示限制查询参数的名字,默认为’limit’
- offset_query_param:表示偏移参数的名字, 默认为’offset’
- max_limit:允许页面中显示的最大数量,默认为None
- template: 渲染分页结果的模板名,默认为”rest_framework/pagination/numbers.html”.
5、游标分页——CursorPagination
基于游标的分页显示了一个不透明的“cursor”指示器,客户端可以使用它来浏览结果集。这种分页方式只允许用户向前或向后进行查询。并且不允许客户端导航到任意位置。
基于游标的分页要求在结果集中有一个惟一的、不变的条目顺序。这个排序通常是记录上的一个创建时间戳,用来表示分页的顺序。
基于游标的分页比其他方案更复杂。它还要求结果集给出一个固定的顺序,并且不允许客户端任意地对结果集进行索引,但是它确实提供了以下好处:
- 提供一致的分页视图。当使用正确的指针分页时,即使在分页过程中其他客户端插入新项时,客户端也不会在分页时看到同一个项两次。
- 支持使用非常大的数据集。大量数据集使用基于off-set的分页方式可能会变得低效或不可用。基于指针的分页模式有固定的时间属性,并且随着数据集的大小的增加而不会减慢。
from rest_framework.pagination import PageNumberPagination, LimitOffsetPagination, CursorPagination
class MyCursorPagination(CursorPagination):
# URL传入的游标参数
cursor_query_param = 'cursor'
# 默认每页显示的数据条数
page_size = 2
# URL传入的每页显示条数的参数
page_size_query_param = 'page_size'
# 每页显示数据最大条数
max_page_size = 1000
# 根据ID从大到小排列
ordering = "id"
6、设置全局配置
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
# 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.CursorPagination',
'PAGE_SIZE': 100
}
显示效果:
四、响应器(response)
五、渲染器
规定页面显示的效果(无用)。
1、局部添加渲染器
urls.py:
from django.conf.urls import url, include
from api.views import course
urlpatterns = [
# path('admin/', admin.site.urls),
url(r'^api/course/$', course.CourseView.as_view()),
]
api/views/course.py:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.renderers import JSONRenderer,BrowsableAPIRenderer
class CourseView(APIView):
# 渲染器
# renderer_classes = [JSONRenderer] # 表示只返回json格式
renderer_classes = [JSONRenderer, BrowsableAPIRenderer] # 数据嵌套在html中展示
def get(self, request, *args, **kwargs):
return Response('...')
显示效果:
2、全局添加渲染器
settings.py:
# 渲染器配置到全局
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer','rest_framework.renderers.BrowsableAPIRenderer']
}
显示效果同上。
六、版本
1、DRF版本控制介绍
API版本控制允许我们在不同的客户端之间更改行为(同一个接口的不同版本会返回不同的数据)。
DRF提供了许多不同的版本控制方案。可能会有一些客户端因为某些原因不再维护了,但是我们后端的接口还要不断的更新迭代,这个时候通过版本控制返回不同的内容就是一种不错的解决方案。
DRF提供了五种版本控制方案如下所示:
from rest_framework import versioning # view中引入版本控制 # 查看 rest_framework/versioning.py文件: class BaseVersioning:... class AcceptHeaderVersioning(BaseVersioning): # 将版本信息放到请求头中 """ GET /something/ HTTP/1.1 Host: example.com Accept: application/json; version=1.0 """ class URLPathVersioning(BaseVersioning): # 将版本信息放入URL中 """ 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 """ class NamespaceVersioning(BaseVersioning): # 通过namespace来区分版本 """ To the client this is the same style as `URLPathVersioning`. The difference is in the backend - this implementation uses Django's URL namespaces to determine the version. An example URL conf that is namespaced into two separate versions # users/urls.py urlpatterns = [ url(r'^/users/$', users_list, name='users-list'), url(r'^/users/(?P<pk>[0-9]+)/$', users_detail, name='users-detail') ] # urls.py urlpatterns = [ url(r'^v1/', include('users.urls', namespace='v1')), url(r'^v2/', include('users.urls', namespace='v2')) ] GET /1.0/something/ HTTP/1.1 Host: example.com Accept: application/json """ class HostNameVersioning(BaseVersioning): # 通过主机名来区分版本 """ GET /something/ HTTP/1.1 Host: v1.example.com Accept: application/json """ class QueryParameterVersioning(BaseVersioning): # 通过url查询参数区分版本 """ GET /something/?version=0.1 HTTP/1.1 Host: example.com Accept: application/json """
1、源码分析拿到版本信息
(1)查看APIView类中的dispatch方法
class APIView(View):
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# 此处做了一个封装
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
self.initial(request, *args, **kwargs)
(2)查看initial方法
class APIView(View):
def initial(self, request, *args, **kwargs):
# Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
可以看到在这里是有version的,也就是版本。
(3)继续查看determine_version方法做了什么
class APIView(View):
def determine_version(self, request, *args, **kwargs):
"""
If versioning is being used, then determine any API version for the
incoming request. Returns a two-tuple of (version, versioning_scheme)
"""
if self.versioning_class is None:
return (None, None)
scheme = self.versioning_class()
return (scheme.determine_version(request, *args, **kwargs), scheme)
1)继续查看versioning_class
class APIView(View):
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
由此可知就是配置了一个类。因此在determine_version中self.versioning_class()是做了一个实例化的动作。
2)return (scheme.determine_version(request, *args, **kwargs), scheme)返回了一个元组
如果在/views/course.py的视图类中定义versioning_class:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning
class CourseView(APIView):
versioning_class = QueryParameterVersioning
def get(self, request, *args, **kwargs):
self.dispatch
return Response('...')
则可以实例化得到scheme实例,并在函数返回语句中返回scheme。
3)进一步查看QueryParameterVersioning中的determine_version
class QueryParameterVersioning(BaseVersioning):
"""
GET /something/?version=0.1 HTTP/1.1
Host: example.com
Accept: 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 version
这里返回的是version,预计就是版本号。
4)进一步查看request.query_params定义
class Request(object):
@property
def query_params(self):
"""
More semantically correct name for request.GET.
"""
return self._request.GET
因此version = request.query_params.get(self.version_param, self.default_version)其实是去URL中获取GET传来的version对应的参数。
5)查看version_param
class BaseVersioning(object):
default_version = api_settings.DEFAULT_VERSION
allowed_versions = api_settings.ALLOWED_VERSIONS
version_param = api_settings.VERSION_PARAM
由此可知这是一个全局配置,默认值就等于version,由此可知前面返回的version就是版本号。
(4)version返回,分析inital方法
def initial(self, request, *args, **kwargs):
# Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
version是版本,scheme是对象并分别赋值给request.version和request.scheme。
(5)在视图类中拿到版本
class CourseView(APIView):
versioning_class = QueryParameterVersioning
def get(self, request, *args, **kwargs):
print(request.version)
return Response('...')
(6)页面访问测试
1)直接访问
此时python后台输出:none
2)用url参数传递版本
此时python后台输出:v1
2、限制版本及源码解析
(1)查看QueryParameterVersioning类
class QueryParameterVersioning(BaseVersioning):
"""
GET /something/?version=0.1 HTTP/1.1
Host: example.com
Accept: 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 version
可以看到version拿到后,用self.is_allowed_version方法做了一个判断。
(2)查看is_allowed_version方法
class BaseVersioning(object):
default_version = api_settings.DEFAULT_VERSION
allowed_versions = api_settings.ALLOWED_VERSIONS
version_param = api_settings.VERSION_PARAM
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))
可以看到ALLOWED_VERSIONS也是存放在配置文件中。
(3)在settings.py中添加允许的版本
# 渲染器配置到全局
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer'],
'ALLOWED_VERSIONS': ['v1', 'v2'] # 允许的版本
}
(4)访问验证
3、默认版本与版本参数
settings.py做如下配置
# 渲染器配置到全局
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer'],
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version', # 把默认的version修改为其他参数:http://127.0.0.1:8000/api/course/?versionsss=v1
'DEFAULT_VERSION': 'v1', # 默认版本
}
(1)如果修改参数为其他值访问效果如下
(2)配置默认版本后不写版本参数也可获取默认版本
python后台输出:v1。
4、配置版本控制
(1)局部版本控制
前面都是在单个类中配置了版本控制,如下所示:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning
class CourseView(APIView):
versioning_class = QueryParameterVersioning
def get(self, request, *args, **kwargs):
self.dispatch
return Response('...')
(2)全局版本控制
源码查看到全局版本控制配置信息:
class APIView(View):
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
# Allow dependency injection of other settings to make testing easier.
settings = api_settings
因此也可以在settings.py中配置全局版本控制:
# 渲染器配置到全局
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': ['rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer'],
'ALLOWED_VERSIONS': ['v1', 'v2'], # 允许的版本
'VERSION_PARAM': 'version', # 把默认的version修改为其他参数:http://127.0.0.1:8000/api/course/?versionsss=v1
'DEFAULT_VERSION': 'v1', # 默认版本
'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.QueryParameterVersioning', # 全局版本控制
}
显示效果如下:
5、写版本推荐方式——基于url的正则方式(如:/v1/users/)
前面写的是基于url的get传参方式,如:/users?version=v1,但是这种方式显示版本不是最推荐的。一般需要把版本号写在前面。改写需要调整urls.py配置。
(1)项目urls.py修改如下:
from django.conf.urls import url, include
urlpatterns = [
# path('admin/', admin.site.urls),
url(r'^api/', include('api.urls')),
]
(2)应用目录下创建urls.py文件,配置如下:
from django.conf.urls import url, include
from api.views import course
urlpatterns = [
url(r'^(?P<version>[v1|v2]+)/course/$', course.CourseView.as_view()),
]
(3)修改/api/views/course.py类视图文件如下所示:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.versioning import QueryParameterVersioning, URLPathVersioning
class CourseView(APIView):
versioning_class = URLPathVersioning
def get(self, request, *args, **kwargs):
print(request.version)
return Response('...')
(4)访问显示效果
以后都推荐用这种方式写版本,全局配置修改同上。
6、其他版本使用方式
详见:http://www.cnblogs.com/wupeiqi/articles/7805382.html
基于 accept 请求头方式,如:Accept: application/json; version=1.0
基于主机名方法,如:v1.example.com
基于django路由系统的namespace,如:example.com/v1/users/