Django REST framework 源码剖析(1)url及对应执行的函数

117 阅读3分钟

 

在url.py中采用CBV的方式及DRF框架提供的as_view()方法,

path("drfcbv/", views.drfcbv.as_view()),

在此处会调用DRF框架提供的as_view()方法

@classmethod

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

可见DRF的as_view方法的返回值是view,而view 又被赋值

 

view = super().as_view(**initkwargs)

此处的view其实调用了django提供的as_view(注意此处调用的是django的而非DRF的),如下

#django
@classonlymethod

def as_view(cls, **initkwargs):

     """Main entry point for a request-response process."""

 for key in initkwargs:

        if key in cls.http_method_names:

            raise TypeError(

                "The method name %s is not accepted as a keyword argument "

                "to %s()." % (key, cls.__name__)

            )

        if not hasattr(cls, key):

            raise TypeError(

                "%s() received an invalid keyword %r. as_view "

                "only accepts arguments that are already "

                "attributes of the class." % (cls.__name__, key)

            )



    def view(request, *args, **kwargs):
        self = cls(**initkwargs)

        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



    # __name__ and __qualname__ are intentionally left unchanged as

    # view_class should be used to robustly determine the name of the view

    # instead.

    view.__doc__ = cls.__doc__

    view.__module__ = cls.__module__

    view.__annotations__ = cls.dispatch.__annotations__

    # Copy possible attributes set by decorators, e.g. @csrf_exempt, from

    # the dispatch method.

    view.__dict__.update(cls.dispatch.__dict__)



    # Mark the callback if the view class is async.

    if cls.view_is_async:

        markcoroutinefunction(view)



    return view

我们需要关注的是返回值,django的as_view中实现了一个闭包,返回的是内部定义的view()函数,因此我们在url.py文件中,我们使用CBV的方法自定义的路径所对应的函数其实是django中的as_view()中的view()。

 

通俗的讲:

我们在访问我们自定义的路径所执行的函数其实就是django内部as_view()返回的view()所返回的函数。

 

#django的as_view()
@classonlymethod

def as_view(cls, **initkwargs):

     """Main entry point for a request-response process."""

 for key in initkwargs:

        if key in cls.http_method_names:

            raise TypeError(

                "The method name %s is not accepted as a keyword argument "

                "to %s()." % (key, cls.__name__)

            )

        if not hasattr(cls, key):

            raise TypeError(

                "%s() received an invalid keyword %r. as_view "

                "only accepts arguments that are already "

                "attributes of the class." % (cls.__name__, key)

            )



    def view(request, *args, **kwargs):

        self = cls(**initkwargs)     #此处实例化了自定义类

        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__

            )

  

        #调用dispatch()对request等参数进行处理

        return self.dispatch(request, *args, **kwargs)



    view.view_class = cls

    view.view_initkwargs = initkwargs



    # __name__ and __qualname__ are intentionally left unchanged as

    # view_class should be used to robustly determine the name of the view

    # instead.

    view.__doc__ = cls.__doc__

    view.__module__ = cls.__module__

    view.__annotations__ = cls.dispatch.__annotations__

    # Copy possible attributes set by decorators, e.g. @csrf_exempt, from

    # the dispatch method.

    view.__dict__.update(cls.dispatch.__dict__)



    # Mark the callback if the view class is async.

    if cls.view_is_async:

        markcoroutinefunction(view)



    return view

 

 

 

#在django框架中
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)

如果是只使用django的话,会调用django的dispatch(),此处的值就是一个函数了(就是我们通过CBV的方法在views.py中定义的类的get(),post(),put()等方法)。

 

 

但是我们在view.py中继承的父类是DRF的APIView,DRF框架中也有dispatch(),按照执行顺序,在django的as_view()的view()的return会找到DRF框架的dispatch(),如下

 

#DRF的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 = 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

 

此处获取传进来的参数,重要的是self.request是被DRF修改过的,不是原先的request,其余的执行get(),post()对应的函数与django框架的类似,不再赘述。

 

 

并且在DRF的as_view()的最后一句

return csrf_exempt(view)

免除了django自带的csrf验证

 

所以的所以,

DRF框架对django的as_view()进行封装,使用自己的dispatch()对request及其余参数进行处理后才执行。