drf中的request与django中的request

79 阅读3分钟

在drf中有request对象,在django中也有request对象,但是drf中的request对象比django中多了很多的成员。具体是为什么多出来的,在哪里多出来的,怎么看多出来哪些,今天就是要记录这个笔记了。

在之前我们知道python中反射(getattr内置函数)的原理了,也知道了在django的CBV中的各个函数的关系,也知道了drf中CBV的各个函数的关系。

不过在这里还是再来重新梳理一下django中从路由到视图函数的这个过程。以在视图函数中定义了一个InfoView类为例:

# urls.py
urlpatterns = [
	path('info/', views.InfoView.as_view(), name='info')
]

# views.py
class InfoView(View):
	def get(self. request):
		pass
  1. 首先请求过来后,会先实例化一个InfoView对象,然后去访问InfoView中的as_view的返回结果。
  2. InfoView中并没有as_view这个方法,所有自动去InfoView__mro__的下一个类,也就是View中找as_view这个成员,找到了。
  3. Viewas_view的方法中,返回的是一个叫做view的内部函数,也就是个闭包。这个view函数会接受请求中的所有参数,包括request对象。
  4. 也就是说请求过来后,views.InfoView.as_view()返回的是View类中as_view方法中的这个view函数。所有请求过来后,就会去执行这里的view函数。
  5. 而这个view函数执行后,返回的是self.dispatch这个方法的返回值。而InfoView的实例中并没有这个方法,于是又去它的父类View中去找这个方法。
  6. View中的dispatch会去找到self中的request.method作为handler,如果找到了,就去执行handler(request,*args,**kwargs),找不到就执行django默认的一个方法。
sequenceDiagram

    autonumber

    请求 ->> django:localhost:8000/info/

    django ->> views.InfoView:去找这个类

    views.InfoView ->> views.InfoView:调用这个as_view()方法,找不到

    views.InfoView ->> View:就去父类中调用as_view()方法

    View -->> django:返回view函数,这里是闭包中的内函数

    django ->> View:执行view函数

    View ->> View:view函数执行返回self.dispathch的执行结果,,这个过程中实例化了一个views.InfoView对象

    View ->> views.InfoView:去实例中找self.dispatch来执行

    views.InfoView ->> View:实例中没有,去父类的找dispatch的执行结果

    View ->> View:dispatch执行返回的是handler()的执行结果

    View -->> views.InfoView:去实例中找到self.request.method作为handler

    views.InfoView -->> views.InfoView:调用self.request.method

    views.InfoView -->> django:返回响应

    django -->> 请求: 

到这里,实际上就完成了django中一个请求在CBV方式下的处理和返回了。

那么drf中有什么不同呢?主要是drf中有一个新的APIView类。在drf中在视图函数中的类是从APIView类中继承的,而这个APIView中重写了View中的viewdispatch两个方法。

sequenceDiagram

    autonumber

    请求 ->> django:localhost:8000/info/

    django ->> views.InfoView:去找这个类

    views.InfoView ->> views.InfoView:调用这个as_view()方法,找不到

    views.InfoView ->> APIView:就去父类中调用as_view()方法

    APIView ->> View:view = super.as_view()

    View -->> APIView:把父类中as_view的执行结果了APIView的view

    APIView -->> django:返回view函数,这里是闭包中的内函数,注意这里是去除了django的csrf的view

    django ->> View:执行view函数

    View ->> View:view函数执行返回self.dispathch的执行结果,这个过程中实例化了一个views.InfoView对象

    View ->> views.InfoView:去实例中找self.dispatch来执行

    views.InfoView ->> APIView:实例中没有,去父类的找dispatch的执行结果

    APIView ->> APIView:dispatch执行返回的是handler()的执行结果

    APIView -->> views.InfoView:去实例中找到self.request.method作为handler

    views.InfoView -->> views.InfoView:调用self.request.method

    views.InfoView -->> django:返回响应

    django -->> 请求: 

从上面可以看到APIView中的view实际上还是View中的view,但是APIView中的dispatch做了下面的代码的事情:

def dispatch(self, request, *args, **kwargs):  
	self.args = args 
    self.kwargs = kwargs  
    request = self.initialize_request(request, *args, **kwargs)  # 把django中的request作为参数传递给了drf中的request
    self.request = request  
    self.headers = self.default_response_headers  
    response = handler(request, *args, **kwargs)  
    self.response = self.finalize_response(request, response, *args, **kwargs)  # 把实例中的响应作为参数赋值给了最终的响应
    return self.response

在drf的request.py中它新建了一个Request类,而drf中的request就是这个类的一个实例。这个Request类中比django中自带的类要多很多的成员,比如data、query_params等等,可以去drf中的request.py文件中去看到。

但是django本身的request对象中有的成员,在drf中也是可以直接访问的。因为在drf的Request这个类中,它将django的request赋值给了_request。同时在这个类中它定义了__getattr__函数,意思就是如果在drf的Request中没找到成员,就去_request中去找,而_request正好是django中的request对象,所以在drf中用request.method就是直接去获取django中的requestmethod方法,当然也可以用request._request.method,是一个意思。下面是精简后的代码。

class Request:  
        self._request = request  
        
		def __getattr__(self, attr):  
			try:  
		        _request = self.__getattribute__("_request")  
		        return getattr(_request, attr)  
		    except AttributeError:  
		        raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{attr}'")