zoukankan      html  css  js  c++  java
  • 版本控制

    版本控制

    一:搭建搭建mvc模型事例

    1.urls.py

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^users/$', views.UsersView.as_view()),
    ]
    

    2.views.py

    from rest_framework.views import APIView
    from django.shortcuts import HttpResponse
    from django.views import View
    
    class UsersView(APIView):
    
        def get(self,request,*args,**kwargs):
            print("GET")
            return HttpResponse("GET请求")
        def post(self,request,*args,**kwargs):
            print("POST")
            return HttpResponse("POST请求")
    

    3.页面渲染效果

    二、源码剖析

    1.请求进来到rest_api到url执行了as_view()方法:

    as_view()
    def as_view(cls, **initkwargs):
        """
        Store the original class on the view function.
    
        This allows us to discover information about the view when we do URL
        reverse lookups.  Used for breadcrumb generation.
        """
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation
    
        view = super(APIView, cls).as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs
    
        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        
    	return csrf_exempt(view)
    

    这段代码主要是将原始的类封装在视图函数上,经过url的分发执行视图函数,为什么我们可以发送直接发送post请求也是因为该方法最后一句代码:

    return csrf_exempt(view) #不需要csrf_token
    

    2.执行视图函数---APIView:

    def dispatch(self, request, *args, **kwargs)方法:
    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 = self.initialize_request(request, *args, **kwargs)
    	
        self.request = request
        self.headers = self.default_response_headers  # deprecate?
    	
    	
        try:
    		#第二步初始化
            self.initial(request, *args, **kwargs)
    		#第三步执行我们的视图函数中对应的请求方式的函数(get,post,put,...)
            # 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
    

    在dispatch方法中执行了完成了很多工作,而版本的注册就是在封装完
    request就执行的
    下面就将封装request剖析:

    第一步:封装request

    a.进入request = self.initialize_request(request, *args, **kwargs)的中:

    def initialize_request(self, request, *args, **kwargs):
        """
        Returns the initial request object.
        """
    
    	##将传参和当前的视图封装返回
        parser_context = self.get_parser_context(request)
    	
    	##下面就是request中封装的方法
        return Request(
            request,
            parsers=self.get_parsers(),
            authenticators=self.get_authenticators(),
            negotiator=self.get_content_negotiator(),
            parser_context=parser_context
        )
    

    b.封装后的request中执行有下面这些方法:

    1. request
    2. get_parsers()
    3. get_authenticators()
    4. get_content_negotiator()
    5. parser_context

    下面接着一个个的去查看这些方法中做了什么:

    get_parsers()

        def get_parsers(self):
        """
        Instantiates and returns the list of parsers that this view can use.
        """
        return [parser() for parser in self.parser_classes]
    

    这里就得到get_parsers() = [ parser(), ]

    get_authenticators()

    def get_authenticators(self):
        """
        Instantiates and returns the list of authenticators that this view can use.
        """
        return [auth() for auth in self.authentication_classes]
    

    这里就得到get_authenticators() = [ auth(), ]

    get_content_negotiator()

    def get_content_negotiator(self):
        """
        Instantiate and return the content negotiation class to use.
        """
        if not getattr(self, '_negotiator', None):
            self._negotiator = self.content_negotiation_class()
        return self._negotiator
    

    这里返回值就是self._negotiator

    parser_context

    def get_parser_context(self, http_request):
        """
        Returns a dict that is passed through to Parser.parse(),
        as the `parser_context` keyword argument.
        """
        # Note: Additionally `request` and `encoding` will also be added
        #       to the context by the Request object.
        return {
            'view': self,
            'args': getattr(self, 'args', ()),
            'kwargs': getattr(self, 'kwargs', {})
        }
    

    封装了当前的view和传入的参数

    上述的方法所处理的功能如下

  • parsers:
  • 解析用户请求数据
  • authenticators
  • 用户认证相关
  • negotiator
  • 选择相关
  • parser_context
  • 封装self和传入的参数

    第二步:初始化

    a.self.initial()

    def initial(self, request, *args, **kwargs):
        """
        Runs anything that needs to occur prior to calling the method handler.
        """
    	#拿url的后缀:例(127.0.0.1:8000/users.json中的json)
        
    	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)
    

    1.版本的处理

    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)
        """
    	#这里self.versioning_class就是返回的版本控制的配置
    	
        if self.versioning_class is None:
            return (None, None)
        scheme = self.versioning_class()
        return (scheme.determine_version(request, *args, **kwargs), scheme)
    

    self.versioning_class

    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
        authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
        throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES
        permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
        content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
        metadata_class = api_settings.DEFAULT_METADATA_CLASS
        
    	#可以知道这个是写在配置文件中
    	versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
    ......
    

    所以我们可以在视图函数中打印这个东西是什么:

    Starting development server at http://127.0.0.1:8000/
    Quit the server with CTRL-BREAK.
    	
    None
    GET
    

    可以知道这个默认的配置是空

    那么问题来了在def determine_version中的self.versioning_class中执行了什么?

        if self.versioning_class is None:
            return (None, None)
        scheme = self.versioning_class()
        return (scheme.determine_version(request, *args, **kwargs), scheme)
    

    从这段源码可以知道
    self.versioning_class()
    应该是个类,且他下面还有一个determine_version方法

    这个方法被封装在下面这个文件中

    from rest_framework.versioning import URLPathVersioning
    

    URLPathVersioning

    在这些个类中去查哪个类有determine_version方法

    class QueryParameterVersioning(BaseVersioning):
        invalid_version_message = _('Invalid version in query parameter.')
    	
    	#determine_version方法
        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
    
        def 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
    

    进行到这里我们就可以在视图中versioning_class关联到QueryParameterVersioning上如下:

    from rest_framework.views import APIView
    from django.shortcuts import HttpResponse
    from rest_framework.versioning import QueryParameterVersioning
    from django.views import View
    
    class UsersView(APIView):
        versioning_class = QueryParameterVersioning
        def get(self,request,*args,**kwargs):  
            print(self.versioning_class )
            print("GET")
            return HttpResponse("GET请求")
        def post(self,request,*args,**kwargs):
            print("POST")
            return HttpResponse("POST请求")
    

    执行结果如下:

    Starting development server at http://127.0.0.1:8000/
    Quit the server with CTRL-BREAK.
    <class 'rest_framework.versioning.QueryParameterVersioning'>
    GET
    [23/Nov/2017 21:37:16] "GET /users/ HTTP/1.1" 200 9
    

    打印的是rest_framework.versioning.QueryParameterVersioning

    所以分到这里我们就知道在版本解析的源码代码中:

        #处理版本信息:版本,处理版本的类的对象
        version, scheme = self.determine_version(request, *args, **kwargs)
        request.version, request.versioning_scheme = version, scheme
    

    返回了两个值version, scheme很明显version这个就是当前的url的版本,而version又被赋值给request.version,所以可以通过打印这个request.version看看其默认的版本是多少

     print(request.version)
    

    结果:

    [23/Nov/2017 21:54:48] "GET /users/ HTTP/1.1" 200 9
    
    None
    

    这里显示是None,这是因为我们在url中根本就没有传版本号,那么问题又来了在url中我们应该怎么传版本呢??

    还是只能去看源码---QueryParameterVersioningdetermine_version方法

    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
    	##这里request.query_params.get=request.GET.get
    

    这段代码的执行结果就是在的get请求中的url中找
    self.version_param这个k的值,很明显这个k是默认的是QueryParameterVersioning这个类中的注释中有说明如下:

    class QueryParameterVersioning(BaseVersioning):
        """
    	#默认的是version
        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
    

    这里我们可以验证:

    print(request.version)
    

    执行的结果如下

    [23/Nov/2017 21:54:48] "GET /users/ HTTP/1.1" 200 9
    GET
    None
    GET
    8
    [23/Nov/2017 22:14:48] "GET /users/?version=8 HTTP/1.1" 200 9
    

    版本号取到是8

    所以对于版本号我们定制的方式可以是url传值的方式对应的url和视图函数的书写如下:

    views.py

    from rest_framework.views import APIView
    from django.shortcuts import HttpResponse
    
    from rest_framework.versioning import QueryParameterVersioning   #导入这个类
    from django.views import View
    
    class UsersView(APIView):
        versioning_class = QueryParameterVersioning  #实例化这个类的对象
        def get(self,request,*args,**kwargs):
    
            print("GET")
            print(request.version) #取版本号的方式
            return HttpResponse("GET请求")
        def post(self,request,*args,**kwargs):
            print("POST")
            return HttpResponse("POST请求")
    

    urls.py

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^users/$', views.UsersView.as_view()),
    ]
    

    同时在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.')
    	
    
    	#版本的方法kwargs
        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
            )
    

    而这个类中的determine_version方法是在请求中的参数取得,请求中的参数则我们能到就是在url中做正则就可以如:

    urls.py

    from django.conf.urls import url
    from django.contrib import admin
    from app01 import views
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^(?P<version>w+)/users/$', views.UsersView.as_view(),name='xxxx'),]
    

    views.py

    from rest_framework.views import APIView
    from django.shortcuts import HttpResponse
    from rest_framework.versioning import URLPathVersioning
    from django.views import View
    
    class UsersView(APIView):
        versioning_class = URLPathVersioning
        def get(self,request,*args,**kwargs):
    
            print("GET")
            print(request.version)
            return HttpResponse("GET请求")
        def post(self,request,*args,**kwargs):
            print("POST")
            return HttpResponse("POST请求")
    

    执行:

    结果:

    Starting development server at http://127.0.0.1:8000/
    Quit the server with CTRL-BREAK.
    GET
    V1
    [23/Nov/2017 22:26:08] "GET /V1/users/ HTTP/1.1" 200 9
    

    版本取到v1

    同时这里也可以对版本进行限制,方法在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.
    	  #这里示例只有v1和v2才能访问
        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
    """
    ........
    

    验证:

    所以对于版本rest_api中共提供的方法都在如下

    全局配置

查看全文
  • 相关阅读:
    Nhibernate代码生成器v2.1中文版
    在asp.net中生成16位随机密码
    IIS 启动不了(发生意外错误0x8ffe2740)
    NET代码生成器
    Linux系统
    VS2005快捷键大全
    ASP+ACCESS数据库中文乱码问题解决
    如何配置ASP.NETOracle 9i 远程登陆数据库
    ASP.NET获取汉字拼音的首字母
    checkbox 实时操作,勾选后变色[带演示]
  • 原文地址:https://www.cnblogs.com/lijian-22huxiaoshan/p/7887623.html
  • Copyright © 2011-2022 走看看