rest-framework框架——解析器、ur控制、分页、响应器、渲染器、版本

一、解析器(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/

 

转载于:https://www.cnblogs.com/xiugeng/p/9595715.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值