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对象进行封装,添加了根据请求来源返回不同结果的功能

 posted on 2020-07-06 20:31  wwwpy  阅读(215)  评论(0编辑  收藏  举报