drf02 深入探索 APIView源码
目录
一、APIView源码剖析
1.CBV源码分析
1.1 引入View
# 先读View的源码
from django.views import View
class Books(View):
def get(self,request):
return HttpResponse('ok')
def book(request):
...
# urls.py
path('book_fbv/', views.book), # fvc 是通过拿到函数的内存地址
path('books_cbv/', views.Books.as_view()) # 那么as_view() 方式拿到的也是一个函数的内存地址
放了一个view的内存地址(View--》as_view--》内层函数)
1.2 View的as_view()方法
# 这是一个闭包函 :在一个外函数中定义了一个内函数,内函数里运用了外函数的临时变量,并且外函数的返回值是内函数的引用。这样就构成了一个闭包。
@classonlymethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
# cls 拿到的是调用这个方法的类 也就是 Books
# 然后根据这个调用类 生成一个对象
self = cls(**initkwargs)
# 用这个对象去调用 dispatch 方法
# self 三步走 对象(无) 调用类(无) 父类(有一个dispatch方法)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
return view
1.3 View的dispatch()方法
http_method_names =["get","post","put","patch","delete","head","options","trace",]
def dispatch(self, request, *args, **kwargs):
# 用来判断是否有这些方法
if request.method.lower() in self.http_method_names:
# 通过反射 getattr方法去拿结果,如果有,返回该方法的函数的内存地址,否则执行其它方法
# 比如 你的请求是get方法
# self 三步走 对象(无) 调用类(get方法) 父类(无)
# hanler 拿到 get的内存地址
handler = getattr(
self, request.method.lower(), self.http_method_not_allowed
)
else:
handler = self.http_method_not_allowed
# 得到方法后 加括号调用该方法,得到其return方法,返回给view() 然后再返回给 as_view()
# 调用get方法 get(request) 返回 return HttpResponse('ok')
return handler(request, *args, **kwargs)
2.APIView源码分析
2.1引入APIView
# 先读View的源码
from rest_framework.views import APIView
class BooksAPIView(APIView):
def post(self, request):
print(request.data) # urlencoded方式有数据,json格式也有,formdata也有数据
print(request.data.get('name'))
print(type( request.FILES.get('file')))
with open('xx.png','wb') as f:
for i in request.FILES.get('file'):
f.write(i)
# print(type(request.POST)) # 原生的POST
# print(request._request.POST)
from django.http.request import QueryDict
return HttpResponse('ok')
...
# urls.py
path('books_apiview/', views.BooksAPIView.as_view()) # 看到as_view() 可以直到拿到的应该是函数的内存地址。
2.2 APIView的as_view()方法
fromdjango.views.generic.base.View
class APIView(View):
@classmethod
def as_view(cls, **initkwargs):
# 获取父类 View 的as_view() - 需要再走一遍CBV源码 不同之处就在其中
view = super().as_view(**initkwargs)
# 为什么view 是一个函数,还能添加属性呢?一切皆对象
view.cls = cls
view.initkwargs = initkwargs
# from django.views.decorators.csrf import csrf_exempt
# csrf: 请求伪造 csrf_exempt: 全局加 局部不加
# 这段代码是什么意思呢? 表示所有APIView.as_view() 的方法都不用csrf方法
return csrf_exempt(view)
2.3 View的as_view()方法
@classonlymethod
def as_view(cls, **initkwargs):
def view(request, *args, **kwargs):
# cls 拿到的是调用这个方法的类 也就是 Books
# 然后根据这个调用类 生成一个对象
self = cls(**initkwargs)
# 关键变化所在
# 用这个对象去调用 dispatch 方法
# self 三步走 对象(无) 调用类(无) 父类(APIView.dispatch) 父类的父类(有)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
return view
2.4 APIView的dispatch()方法
def dispatch(self, request, *args, **kwargs):
self.args = args
self.kwargs = kwargs
# 这个是重点 - 重新包装成一个request对象,以后再用的request对象,就是新的request对象了
# 总结原来的方法还能向原来的方式去用,并封装了其它的方法 如data query_params()
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
2.4.1 APIView的dispatch().initialize_request()
def initialize_request(self, request, *args, **kwargs):
# 对原本的request数据进行封装
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
from rest_framework.request import Request
# 只要继承了APIView,视图类中的request对象,都是新的,也就是上面那个request的对象了
# 老的request在新的request._request
# 以后使用reqeust对象,就像使用之前的request是一模一样(因为重写了__getattr__方法)
def __getattr__(self, attr):
try:
return getattr(self._request, attr) #通过反射,取原生的request对象,取出属性或方法
except AttributeError:
return self.__getattribute__(attr)
# request.data 感觉是个数据属性,其实是个方法,@property,修饰了
它是一个字典,post请求不管使用什么编码,传过来的数据,都在request.data
#get请求传过来数据,从哪取?
request.GET
@property
def query_params(self):
"""
More semantically correct name for request.GET.
"""
return self._request.GET
#视图类中
print(request.query_params) #get请求,地址中的参数
# 原来在
print(request.GET)
2.4.2 APIView的dispatch().initial()
def initial(self, request, *args, **kwargs):
# 控制api版本,如果需要控制版本可以使用这方法
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# 认证组件:校验用户 - 游客、合法用户、非法用户
# 游客:代表校验通过,直接进入下一步校验(权限校验)
# 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验)
# 非法用户:代表校验失败,抛出异常,返回403权限异常结果
self.perform_authentication(request)
# 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色
# 认证通过:可以进入下一步校验(频率认证)
# 认证失败:抛出异常,返回403权限异常结果
self.check_permissions(request)
# 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s)
# 没有达到限次:正常访问接口
# 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问
self.check_throttles(request)
2.5总结 - 流程图


浙公网安备 33010602011771号