在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
- 首先请求过来后,会先实例化一个
InfoView对象,然后去访问InfoView中的as_view的返回结果。 InfoView中并没有as_view这个方法,所有自动去InfoView的__mro__的下一个类,也就是View中找as_view这个成员,找到了。- 在
View的as_view的方法中,返回的是一个叫做view的内部函数,也就是个闭包。这个view函数会接受请求中的所有参数,包括request对象。 - 也就是说请求过来后,
views.InfoView.as_view()返回的是View类中as_view方法中的这个view函数。所有请求过来后,就会去执行这里的view函数。 - 而这个
view函数执行后,返回的是self.dispatch这个方法的返回值。而InfoView的实例中并没有这个方法,于是又去它的父类View中去找这个方法。 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中的view和dispatch两个方法。
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中的request的method方法,当然也可以用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}'")