• Django rest framework源码分析(4)----版本


     新建一个工程Myproject和一个app名为api

    (1)api/models.py

    from django.db import models
    
    class UserInfo(models.Model):
        USER_TYPE = (
            (1,'普通用户'),
            (2,'VIP'),
            (3,'SVIP')
        )
    
        user_type = models.IntegerField(choices=USER_TYPE)
        username = models.CharField(max_length=32,unique=True)
        password = models.CharField(max_length=64)
        group = models.ForeignKey('UserGroup',on_delete=models.CASCADE)
        roles = models.ManyToManyField('Role')
    
    
    class UserToken(models.Model):
        user = models.OneToOneField('UserInfo',on_delete=models.CASCADE)
        token = models.CharField(max_length=64)
    
    
    class UserGroup(models.Model):
        title = models.CharField(max_length=32)
    
    
    class Role(models.Model):
        title = models.CharField(max_length=32)
    

      

    (2)Myproject/urls.py

    from django.contrib import admin
    from django.urls import path,include
    
    urlpatterns = [
        #path('admin/', admin.site.urls),
        path('api/',include('api.urls') ),
    ]
    

      

    (3)api/urls.py

    # api/urls.py
    
    from django.urls import path
    from .views import UserView
    
    urlpatterns = [
        path('users/', UserView.as_view()),
    ]
    

      

    (4)views.py

    # api/views.py
    
    from django.shortcuts import render,HttpResponse
    from rest_framework.views import APIView
    from rest_framework.request import Request
    from rest_framework.versioning import QueryParameterVersioning
    
    class UserView(APIView):
    
        versioning_class = QueryParameterVersioning
    
        def get(self,request,*args,**kwargs):
            #获取版本
            print(request.version)
            return HttpResponse('用户列表')
    

      

    (5)settings.py

    #版本
    REST_FRAMEWORK = {
        "DEFAULT_VERSION":'v1',               #默认的版本
        "ALLOWED_VERSIONS":['v1','v2'],       #允许的版本
        "VERSION_PARAM":'version'             #GET方式url中参数的名字  ?version=xxx
    }
    

      

     1.url中通过GET传参

    QueryParameterVersioning用于去GET参数中取version

    http://127.0.0.1:8000/api/users/?version=v2
    

      

    后台可以看到当前的版本

     如果url中没有传版本参数,则显示默认的版本("DEFAULT_VERSION":'v1')

    http://127.0.0.1:8000/api/users/
    

      

     如果url传的版本超过settings中的允许范围则报错

    http://127.0.0.1:8000/api/users/?version=v3
    

      

    2.在URLPATH中获取

     (1)修改api/urls.py

    通常情况我门应该用URLPATH的方式,而不是用前面GET()传参方式

    url里面通过正则表达式定义哪些版本,

    # 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()),
    ]
    

      

    (2)views.py

    URLPathVersioning:去url路径里面获取版本
    # api/views.py
    
    from django.shortcuts import render,HttpResponse
    from rest_framework.views import APIView
    from rest_framework.request import Request
    from rest_framework.versioning import URLPathVersioning
    
    class UserView(APIView):
    
        versioning_class = URLPathVersioning
    
        def get(self,request,*args,**kwargs):
            #获取版本
            print(request.version)
            return HttpResponse('用户列表')
    

      

    这个URLPathVersioning我们可以放到settings里面,全局配置,就不用写到views里面,每个类都要写一遍了

    settings.py

    # 版本
    # REST_FRAMEWORK = {
    #     "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
    #     "DEFAULT_VERSION":'v1',               #默认的版本
    #     "ALLOWED_VERSIONS":['v1','v2'],       #允许的版本
    #     "VERSION_PARAM":'version'             #get方式url中参数的名字  ?version=xxx
    # }
    
    #全局
    REST_FRAMEWORK = {
        "DEFAULT_VERSIONING_CLASS":"rest_framework.versioning.URLPathVersioning",
    }
    

      

    修改views.py

    # 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)
            return HttpResponse('用户列表')
    

      

    浏览器访问地址

    http://127.0.0.1:8000/api/v1/users/
    

      

    然后后台拿到版本信息

    3.反向解析访问的url

    (1)api/urls.py

    添加name = 'api_user'

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

      

    (2)views.py

    # 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('用户列表')
    

      

    浏览器访问

    http://127.0.0.1:8000/api/v1/users/
    

      

    后台获取

     源码流程

     (1)dispatch

    def dispatch(self, request, *args, **kwargs):
            """
            `.dispatch()` is pretty much the same as Django's regular dispatch,
            but with extra hooks for startup, finalize, and exception handling.
            """
            self.args = args
            self.kwargs = kwargs
            #对原始request进行加工,丰富了一些功能
            #Request(
            #     request,
            #     parsers=self.get_parsers(),
            #     authenticators=self.get_authenticators(),
            #     negotiator=self.get_content_negotiator(),
            #     parser_context=parser_context
            # )
            #request(原始request,[BasicAuthentications对象,])
            #获取原生request,request._request
            #获取认证类的对象,request.authticators
            #1.封装request
            request = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
    
            try:
                #2.认证
                self.initial(request, *args, **kwargs)
    
                # Get the appropriate handler method
                if request.method.lower() in self.http_method_names:
                    handler = getattr(self, request.method.lower(),
                                      self.http_method_not_allowed)
                else:
                    handler = self.http_method_not_allowed
    
                response = handler(request, *args, **kwargs)
    
            except Exception as exc:
                response = self.handle_exception(exc)
    
            self.response = self.finalize_response(request, response, *args, **kwargs)
            return self.response
    

      

    (2)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.
            #request.version获取版本信息
            #request.versioning_scheme获取处理版本你的对象
            version, scheme = self.determine_version(request, *args, **kwargs)
            request.version, request.versioning_scheme = version, scheme
    
            # Ensure that the incoming request is permitted
            #4.实现认证
            self.perform_authentication(request)
            #5.权限判断
            self.check_permissions(request)
            #6.控制访问频率
            self.check_throttles(request)
    

      

    (3)determine_version

    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)
    

      

    (4)versioning_class

    URLPathVersioning源码

    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
            )
    

      

    可以看到

    (1)url配置

    (2)determine_version

     里面有个is_allowed_version,点进去可以看到一些基本参数 (继承BaseVersioning基类)

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

      

  • 相关阅读:
    你知道线程池是如何退出程序的吗?
    华为云GuassDB(for Redis)发布全新版本推出:Lua脚本和SSL连接加密
    分布式消息流平台:不要只想着Kafka,还有Pulsar
    新来的前端小姐姐问:Vue路由history模式刷新页面出现404问题
    1ms的时延,10Gbps速率…5G通信技术解读
    一分钟带你了解Huawei LiteOS组件开发指南
    资深Linux 系统管理员常用的15个很好用的Cron工作示例
    C语言中动态内存分配的本质是什么?
    Python连载8datetime包函数介绍 心悦君兮君不知
    Python连载7time包的其他函数 心悦君兮君不知
  • 原文地址:https://www.cnblogs.com/wanstack/p/9883935.html
Copyright © 2020-2023  润新知