APIView与Request源码分析
APIView与Request
基于APIView+JsonResponse编写接口
from rest_framework.views import APIView
from django.http import JsonResponse
from .models import Book
# 以前我们继承的是django原生的view现在继承drf的APIView
class BookView(APIView):
def get(self,request):
books = Book.objects.all()
book_list = []
for book in books:
book_list.append({'name':book.name,'price':book.price})
return JsonResponse(book_list,safe=False)
发现按照原本继承原生view的逻辑写法也可以正常编写
基于APIView+Response编写接口
from rest_framework.views import Response
按照上面的代码最后一句return改成
return Response(book_list)
# 不设置safe参数 字典和列表都可以序列化
经过上面的流程体会到了APIView 与Response的方便快捷,但是不知道其底层都干了些什么事情,想要了解还得去查源码
APIView的源码分析
1.经过路由层的匹配成功自动执行'views.BookView.as_view()() '我们发现因为我们继承的是'APIView'这个类,此时会进入'APIView'里查看是否有'as_view'方法,进入查看是有的。而且是绑定给类的方法。
def as_view(cls, **initkwargs):
# 发现其内部super使用子类调用父类方法,因为APIView继承的是原生View,那就是调用了django原生的as_view方法。返回的是as_view内层函数view
"此时的view就是原生django as_view的内层函数view"
view = super().as_view(**initkwargs)
# csrf_exempt 就是排除了所有的csrf认证,把view传过去那么相当于给view加了个语法糖,view内部所有的方法都不用过csrf认证
"我们在执行视图类的各种请求方法其实都是通过了view的内部进行了反射拿到并执行"
return csrf_exempt(view)
2. 路由匹配成功后执行了上面的步骤最后'diango原生的view方法'返回的是'self.dispatch'方法. 我们通过名称空间的查找顺序此时的'self对象'是视图类'BookView'的对象那么我们先从自身查找发现并没有'dispatch'方法,那么去父类'APIView'里查找发现有这个方法,此时执行的是'APIView的dispatch'方法。
def dispatch(self, request, *args, **kwargs):
# 此时的request还是django原生的request,从下面这行代码包装后成为了drf的新的request,“initialize_request”反回了一个新的request
# 老的request被initialize_request方法内部的Response给了对象self._request
request = self.initialize_request(request, *args, **kwargs)
# 把新的request给了视图类BookView的对象
self.request = request
try:
"initial 三大认证【认证,频率,权限】"# 此时的request是新的request
self.initial(request, *args, **kwargs)
# 用反射拿到方法与原生的dispatch方法一致
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
# 新的request传入了视图方法此时视图类方法的request也是新的。
response = handler(request, *args, **kwargs)
except Exception as exc:
# 在执行3大认证和视图类中方法的过程中,如果出了异常,都能捕获到---》全局异常捕获
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
总结
1.祛除了所有的csrf认证
2.包装了新的request并传给了视图类,视图类中的request都是新的,不再是原生的。
3.执行视图类的方法前执行了三大认证
4.如果三大认证与视图类的方法执行过程报了错,都会被异常捕获--全局异常捕获
5.以后视图类的request都是新的。
Resquest源码分析
我们知道经过APIView类的各种方法处理后此时视图类的request是新的,那么老的request去哪里了。
在APIView里发现
from rest_framework.request import Request
就是在dispatch内的initialize_request把request包装成了新的request,返回的Request,并在内部把老的给了self._request.那么此时新的request里面包含了老的request
request._request也能拿到老的。
此时又出现问题了,在视图类方法中执行request.method,研究源码发现新的Request他并没有method方法,但是有一个双下getattr方法,这个魔法方法在.一个不存在的名字时会触发。
def __getattr__(self, attr):
try:
# self._request此时去了老的request反射拿到方法
return getattr(self._request, attr)
except AttributeError:
return self.__getattribute__(attr)
request.data # data方法被伪装成了数据属性 不用加括号直接就可以的调用
"POST,PUT等等请求方法在body内提交的数据,都从request.data中取,取出来就是字典,无论是哪种编码格式"
request.query_params # 也是被伪装成了方法
"get请求携带的参数都从这里面取"
request.FILES # 伪装了
"前端提交过来的文件,从这里面取"
总结
1.新的request用起来与老的一样,新的取不到方法会从老的里面取
2.request.data 只要是在body内的数据都从这里取,取出来就是字典。不论编码请求方式
3.request.query_params 就是原来的request._request.GET
4.上传的文件从 request.FILES里取