CBV源码分析
1,在路由层写的cbv的调用方法是: views.Book.as_view()
这里Book是在视图层写的类,Book继承了view类,该方法是用试图层的Book类(views.Book)调用了view类下的as_view方法,不写参数默认传入当前的request
@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("You tried to pass in the %s method name as a "
"keyword argument to %s(). Don't do that."
% (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)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
# take name and docstring from class
update_wrapper(view, cls, updated=())
# and possible attributes set by decorators
# like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
2,为什么Book的对象能获得request属性?
as_view方法返回了一个view函数(view函数是view类下的as_view类方法的内置函数.
该函数实例化了Book类并通过self.request=request的方法将request给了Book类产生的对象,所以在Book类中写绑定给对象的方法,这个对象就有了request
该方法又返回了self.dispatch函数
def view(request, *args, **kwargs):
self = cls(**initkwargs)
if hasattr(self, 'get') and not hasattr(self, 'head'):
self.head = self.get
self.request = request
self.args = args
self.kwargs = kwargs
return self.dispatch(request, *args, **kwargs)
3,为什么cbv能实现自动调用方法?
由于Book类没有dispatch方法,所以在Book类的父类即view类里找到dispatch方法.
view类有一个列表为值请求方法的小写格式,http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace'],dispatch通过request获取请求方法后转小写判断该方法是否在上述列表里(这里使用self.http_method_names是先判断对象是否有该列表,可通过在Book类中重写该列表来限制请求方法)
如果方法在列表里则通过反射(getattr)来调取Book类的方法,就是你在Book类中定义的名为post,get等方法,如果Book类的方法想要能被调用,这方法名必须和list列表里的名字一致.
一旦调用了Book类的方法就会返回一个Httpresponse给前端.
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)
APIview源码分析
视图层的Book类继承了APIview类,APIview类继承了view类
这种思想的优点在于灵活运用了属性查找和绑定方法的特点,属性查找时可以通过重写父类的功能来实现自己想要达到的限制效果,编写过程大量运用面向对象的思想.
当然也可以通过中间件的方式来达到目的,这就用到了面向过程的思想.
1,当请求来时依然通过路由层调用as_view函数,这是调用的是API的类的函数,该函数调用了父类的as_view方法将结果赋值给自己定义而view函数,且API的as_view函数返回加了csrf_except装饰器的函数,没用语法糖.所以as_view取消了csrf的认证.
#APIView的as_view方法(类的绑定方法)
def as_view(cls, **initkwargs):
view = super().as_view(**initkwargs) # 调用父类(View)的as_view(**initkwargs)
view.cls = cls
view.initkwargs = initkwargs
# 以后所有的请求,都没有csrf认证了,只要继承了APIView,就没有csrf的认证
return csrf_exempt(view)
2,API的view函数本质是view类的view函数,当view函数使用self.的方式调用dispatch时,会先去API的类去查找,结果API有dispatch函数,所以这个dispatch是APi的dispatch.
这里API重写了dispatch方法,该方法有四大功能:
一, 重写包装了一个request对象,并把原来的request作为属性添加到新的request中,而且重写了request对象的__setattr__方法,当使用点获取对象属性时先去原request中查找属性,找不到在去新的对象中差找属性.
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)
二, 加入了initial方法,该方法可实现用户认证,权限认证和频率限制
# APIView的initial方法
def initial(self, request, *args, **kwargs):
# 认证组件:校验用户 - 游客、合法用户、非法用户
# 游客:代表校验通过,直接进入下一步校验(权限校验)
# 合法用户:代表校验通过,将用户存储在request.user中,再进入下一步校验(权限校验)
# 非法用户:代表校验失败,抛出异常,返回403权限异常结果
self.perform_authentication(request)
# 权限组件:校验用户权限 - 必须登录、所有用户、登录读写游客只读、自定义用户角色
# 认证通过:可以进入下一步校验(频率认证)
# 认证失败:抛出异常,返回403权限异常结果
self.check_permissions(request)
# 频率组件:限制视图接口被访问的频率次数 - 限制的条件(IP、id、唯一键)、频率周期时间(s、m、h)、频率的次数(3/s)
# 没有达到限次:正常访问接口
# 达到限次:限制时间内不能访问,限制时间达到后,可以重新访问
self.check_throttles(request)
三,根据请求端返回相应格式数据,浏览器就返回Httpresponse,postman就返回json格式数据
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
四,用反射调用试图层方法
# 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
一旦调用了视图层的类的方法就会返回相应的结果给前端.
APIview就是通过重写dispatch方法实现了添加方法的功能,这就类似于钩子功能.