zoukankan      html  css  js  c++  java
  • django的View和drf的APIView源码解析

    django中的View源码解析

    FBV:    path('admin/', admin.site.urls),
    CBV:    path('books/', views.BookView.as_view()),
    

    在路由层使用CBV时需要执行 CBV.as_view()方法,使该位置的实参变为一个FBV(函数地址)

    as_view方法的返回值是view

    view是一个函数,即CBV的views.BookView.as_view()的结果就是views.BookView.view

    在收到请求,路由匹配成功之后会触发view函数的运行

    view函数 执行了self.dispatch(request, *args, **kwargs)并返回该函数的返回值

    class View:
        def as_view(cls, **initkwargs):
            def view(request, *args, **kwargs):
                self = cls(**initkwargs)
                if hasattr(self, 'get') and not hasattr(self, 'head'):
                    self.head = self.get
                self.setup(request, *args, **kwargs)
                if not hasattr(self, 'request'):
                    raise AttributeError(
                        "%s instance has no 'request' attribute. Did you override "
                        "setup() and forget to call super()?" % cls.__name__
                    )
                return self.dispatch(request, *args, **kwargs)
            view.view_class = cls
            view.view_initkwargs = initkwargs
    
            # take name and docstring from class
            update_wrapper(view, cls, updated=())
    
            # and possible attributes set by decorators
            # like csrf_exempt from dispatch
            update_wrapper(view, cls.dispatch, assigned=())
            return view
    

    进入dispatch方法查看源码

        def dispatch(self, request, *args, **kwargs):
            # Try to dispatch to the right method; if a method doesn't exist,
            # defer to the error handler. Also defer to the error handler if the
            # request method isn't on the approved list.
            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
            return handler(request, *args, **kwargs)
    

    dispatch中对请求方式是否在http_method_names列表中进行了判断

    http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
    

    如果请求方式在这个列表中则进行反射,从我们自定义的CBV中取出与请求方式同名的函数执行,

    如果CBV中没该名字的函数或者请求方式不在该列表中,就会报错,拒绝访问

    drf中APIView源码解析

    FBV:    path('admin/', admin.site.urls),
    CBV:    path('books/', views.BookAPIView.as_view()),
    

    在路由层使用CBV时需要执行 CBV.as_view()方法,使该位置的实参变为一个FBV(函数地址)

    看源码可知执行该方法得到的返回值为csrf_exempt(view)

    装饰器的语法糖的原理就是view=csrf_exempt(view),

    所以返回csrf_exempt(view)也可以看作是 为view添加了一个csrf_exempt装饰器

    `

    APIView继承了django中的View类

    class APIView(View):
        @classmethod
        def as_view(cls, **initkwargs):
            view = super().as_view(**initkwargs)
            view.cls = cls
            view.initkwargs = initkwargs
            return csrf_exempt(view)
    

    在执行APIView的as_view方法时调用了父类的as_view方法,也就是View的as_view方法

    class View:
    	@classonlymethod
        def as_view(cls, **initkwargs):
            def view(request, *args, **kwargs):
                self = cls(**initkwargs)
                if hasattr(self, 'get') and not hasattr(self, 'head'):
                    self.head = self.get
                self.setup(request, *args, **kwargs)
                if not hasattr(self, 'request'):
                    raise AttributeError(
                        "%s instance has no 'request' attribute. Did you override "
                        "setup() and forget to call super()?" % cls.__name__
                    )
                return self.dispatch(request, *args, **kwargs)
            view.view_class = cls
            view.view_initkwargs = initkwargs
    
            # take name and docstring from class
            update_wrapper(view, cls, updated=())
    
            # and possible attributes set by decorators
            # like csrf_exempt from dispatch
            update_wrapper(view, cls.dispatch, assigned=())
            return view
    

    View的as_view方法执行并返回了self.dispatch(request, *args, **kwargs)

    注意:这里的self指代的是自己写的CBV的对象

    所以self.dispatch(request, *args, **kwargs)触发的不是View中的dispatch方法

    按照名称空间的属性查找可以知道,它是先从自己写的CBV的对象中查看是否有dispatch方法

    如果没有则执行父类,也就是APIView中的dispatch方法

    class APIView(View):
        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 = self.initialize_request(request, *args, **kwargs)
            self.request = request
            self.headers = self.default_response_headers  # deprecate?
    
            try:
                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
    

    APIView中的dispatch方法基本的功能和View中的dispatch方法一致,但是添加了一些别的功能:

    1. request的封装
      
      request = self.initialize_request(request, *args, **kwargs)
      self.request = request
      

    对request进行了封装:将原生django中的request传入initialize_request方法,通过实例化drf定义的Request类,产生了一个新的request对象,进行替换,在原本的request对象的功能基础上,额外添加了一些功能,例如request.data属性

    1. 异常处理
      
      except Exception as exc:
              response = self.handle_exception(exc)
      

    对异常进行捕获,使我们可以重写handle_exception方法,返回json格式的数据

    1. response的封装
      
              self.response = self.finalize_response(request, response, *args, **kwargs)
              return self.response
      

    对response对象进行封装,添加了根据请求来源返回不同结果的功能

  • 相关阅读:
    第二章例2-9
    第二章例2-8
    第二章例2-7
    第二章例2-6
    第二章例2-5
    第二章例2-4
    第二章例2-3
    第二章例2-2
    第二章例2-1
    第一章例1-2
  • 原文地址:https://www.cnblogs.com/achai222/p/13257321.html
Copyright © 2011-2022 走看看