zoukankan      html  css  js  c++  java
  • DRF--路由组件和版本控制

    路由组件

    先来看下我们前面写的路由

    from django.conf.urls import url, include
    from .views import  BookModelView
    
    urlpatterns = [
        url(r'^book/$', BookModelView.as_view({"get": "list", "post": "create"})),
        url(r'^book/(?P<pk>d+)', BookModelView.as_view({"get": "retrieve", "put": "update", "delete": "destroy"})),
    ]

    这样写路由也没有什么问题,DRF也提供给了我们另一种写法

    from .views import  BookModelView
    # 帮助我们生成带参数的路由
    from rest_framework.routers import DefaultRouter
    
    # 实例化DefaultRouter对象
    router = DefaultRouter()
    
    # 注册我们的路由以及视图
    router.register(r'^book', BookModelView)
    
    
    urlpatterns = [
       
    ]
    
    # 把我们注册的路由添加到里面
    urlpatterns += router.urls

    我们在浏览器里输入错误的地址看DRF都给我们生成了哪些路由

    ^api/ ^^book/$ [name='book-list']
    ^api/ ^^book.(?P<format>[a-z0-9]+)/?$ [name='book-list']
    ^api/ ^^book/(?P<pk>[^/.]+)/$ [name='book-detail']
    ^api/ ^^book/(?P<pk>[^/.]+).(?P<format>[a-z0-9]+)/?$ [name='book-detail']
    ^api/ ^$ [name='api-root']
    ^api/ ^.(?P<format>[a-z0-9]+)/?$ [name='api-root']

    这样生成的路由需要在路径最后面加 / ,可以在实例化DefaultRouter对象的时候加个参数就可以了

    router = DefaultRouter(trailing_slash=False)  # trailing_slash=False 不需要 / 结尾

    这种注册路由得方法在实际项目中并不常用,因为把这些路由都开放,会造成一定的安全性

     版本控制

    我们的网站要在不同的阶段加入不同的功能,restful规定了我们的接口要有版本号。但是不是所有人看到有更新就会更新的,所以我们的要兼容不同的版本。而前端肯定能拿到用户访问的是哪个版本的,这样,让前端的妹子把版本号传给后端,后端拿到版本号,做不同的处理,在把结果返回给前端就可以了。

    既然我们知道了前端妹子可以拿到版本号,那怎么传给后端,是放在url里还是放在header里,或者是请求体里。这些都可以,妹子怎么高兴怎么来。

    我们先来分析一下DRF里的源码是怎么写的

    进入APIView类里,找到dispatch方法,找到下面的代码

    try:
        self.initial(request, *args, **kwargs)

    进入initial方法里

    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)
    View Code

    源码里会把 determine_version方法的结果解包赋值给version,然后把version赋值给了request.version去看看 determine_version做了什么

    1 def determine_version(self, request, *args, **kwargs):
    2     """
    3     If versioning is being used, then determine any API version for the
    4     incoming request. Returns a two-tuple of (version, versioning_scheme)
    5     """
    6     if self.versioning_class is None:
    7         return (None, None)
    8     scheme = self.versioning_class()
    9     return (scheme.determine_version(request, *args, **kwargs), scheme)
    View Code

    里面判断了versioning_class这个方法的返回值是不是None,默认api_settings里的DEFAULTS里的DEFAULT_VERSIONING_CLASS'为None,所以版本默认就是为None,我们可以写个方法来测试一下

    class VersionDemo(APIView):
        def get(self, request):
            print(request.version)
            print(request.versioning_scheme)
            return Response("ok")

    最后的结果就是None

    所以我们可以重写DEFAULT_VERSIONING_CLASS这个方法。哪我们在哪里重写呢?Django给我们提供了settings.py文件,这里面的会覆盖默认的配置。好了,既然我们已经知道了在哪里重写,那我们就来实践一遍

    全局版本类的配置

    先在项目的根目录下创建个utils文件夹,在创建个version.py文件

     在里面写如下代码

    class MyVersion(object):
        def determine_version(self, request, *args, **kwargs):  # 必须是这个函数名
            # 方法的返回值是版本号
            # 获取前端传过来的版本号,并且把版本号返回
            version = request.query_params.get('version')  # 前端在url里携带版本号
            if not version:
                version = 'v1'
            return version

    写好了去settings.py里配置

    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS":"utils.version.MyVersion"
    }
    
    # utils.version.MyVersion 文件里的类名

    然后访问类似这样的地址:http://127.0.0.1:8000/version/demo/?version=v3,控制台打印出的就是v3。既然我们能拿到版本号了,那我们就能根据版本号做不同的逻辑处理了。修改views下的VersionDemo类

    class VersionDemo(APIView):
        def get(self, request):
            print(request.version)
            print(request.versioning_scheme)
            # 根据版本号处理不同的业务逻辑
            if request.version == 'v2':
                return Response('这是v2的业务逻辑')
            return Response("这是v1的业务逻辑")

    上面的配置是全局的配置,假如我们现在有两个类

    from rest_framework.views import APIView
    from rest_framework.response import Response

    class VersionDemo(APIView):

      def get(self, request): print(request.version) print(request.versioning_scheme) # 根据版本号处理不同的业务逻辑 if request.version == 'v2': return Response('这是v2的业务逻辑') return Response("这是v1的业务逻辑") class VersionTest(APIView): def get(self, request): print(request.version) print(request.versioning_scheme) # 根据版本号处理不同的业务逻辑 if request.version == 'v2': return Response('这是v3的业务逻辑') return Response("这是v4的业务逻辑")

    现在不管访问哪个类都会走我们定义的MyVersion类,有时候,我们只希望一部分类有版本控制,一部分不需要版本控制。DRF可以允许我们局部配置

    局部配置版本号

    局部配置版本号要先注释掉settings.py里写的配置项

    # REST_FRAMEWORK = {
    #     "DEFAULT_VERSIONING_CLASS":"utils.version.MyVersion"
    # }

    然后在需要版本控制的方法前加上 versioning_class=版本类,类似于下面这样

    from django.shortcuts import render
    from rest_framework.views import APIView
    from rest_framework.response import Response
    from utils.version import MyVersion
    
    
    class VersionDemo(APIView):
        versioning_class = MyVersion
    
        def get(self, request):
            print(request.version)
            print(request.versioning_scheme)
            # 根据版本号处理不同的业务逻辑
            if request.version == 'v2':
                return Response('这是v2的业务逻辑')
            return Response("这是v1的业务逻辑")
    
    
    class VersionTest(APIView):
        def get(self, request):
            print(request.version)
            print(request.versioning_scheme)
            # 根据版本号处理不同的业务逻辑
            if request.version == 'v2':
                return Response('这是v3的业务逻辑')
            return Response("这是v4的业务逻辑")

    这样配置好了之后,我们访问 VersionDemo,就会走我们自定义的版本类。如果访问 VersionTest,就不会走我们自定义的版本类,版本号为默认的None

    使用原始的版本配置类

    DRF已经替我们封装好了版本控制类,包括请求头的,url里的,主机里的。。。我们只需要使用就可以了,所有的类都在 versioning 方法里

    from rest_framework import versioning

    使用局部的时候只需要加上versioning_class就可以了,类似于下面这样

    class VersionTest(APIView):
        versioning_class = versioning.QueryParameterVersioning  # 使用自带的配置
        def get(self, request):
            print(request.version)
            print(request.versioning_scheme)
            # 根据版本号处理不同的业务逻辑
            if request.version == 'v2':
                return Response('这是v3的业务逻辑')
            return Response("这是v4的业务逻辑")

    如果要使用全局的配置,就需要在settings.py里进行配置了

    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.QueryParameterVersioning"
    }

    这样就不需要我们自己写版本控制类了

     其他的一些配置

    REST_FRAMEWORK = {
        # 默认使用的版本控制类
        'DEFAULT_VERSIONING_CLASS': 'rest_framework.versioning.URLPathVersioning',
        # 允许的版本
        'ALLOWED_VERSIONS': ['v1', 'v2'],
        # 版本使用的参数名称
        'VERSION_PARAM': 'version',
        # 默认使用的版本
        'DEFAULT_VERSION': 'v1',
    }
  • 相关阅读:
    七种数据类型
    js字符串解析成数字
    html节点操作与事件
    form表单的默认提交行为
    CSS中的各种width(宽度)
    Javascript读写CSS属性
    ECMAScript6新特性之Reflect
    ECMAScript6新特性之String API
    ECMAScript6新特性之Array API
    判断Javascript对象是否为空
  • 原文地址:https://www.cnblogs.com/zouzou-busy/p/11569791.html
Copyright © 2011-2022 走看看