DRF | rest_framework 基层 Views 源码分析
Base.py
class View 下面先创建了一个类属性http_method_names
http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']
一个包含了所有HTTP协议会用到的方法名称的字符串
def __init__(self, **kwargs):
"""
Constructor. Called in the URLconf; can contain helpful extra
keyword arguments, and other things.
"""
1. Go through keyword arguments, and either save their values to our
1. instance, or raise an error.
for key, value in kwargs.items():
setattr(self, key, value)
在类的构造器里面定义一个初始化魔术方法:
for key, value in kwargs.items():
setattr(self, key, value)
我们之前学习过setattr
的魔术是setattr(x, 'y', v) is equivalent to ``x.y = v
紧接着在类方法里 定义as_view()
as_view()
@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(
'The method name %s is not accepted as a keyword argument '
'to %s().' % (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)
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
view.view_class = cls
view.view_initkwargs = initkwargs
1. take name and docstring from class
update_wrapper(view, cls, updated=())
1. and possible attributes set by decorators
1. like csrf_exempt from dispatch
update_wrapper(view, cls.dispatch, assigned=())
return view
经过@classonlymethod
装饰器的装饰后,as_veiw()解读:
for key in initkwargs:
if key in cls.http_method_names:
raise TypeError(
'The method name %s is not accepted as a keyword argument '
'to %s().' % (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))
initkwar
是类构造器里面传入的参数,通过key遍历在for列表里面循环:http_method_names
里面的名字。
如果存在就捕获不接受方法名称作为关键字参数
。如果不在就捕获收到一个无效的关键字 %r。只接受已经是类属性的参数。
接下来走到我们要看的view()
方法
def view(request, *args, **kwargs):
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, 'request'):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
return self.dispatch(request, *args, **kwargs)
如果对象没有request
方法,捕获异常。如果有,通过return调用dispatch
方法。
什么是 dispatch
方法呢?
dispatch()
def dispatch(self, request, *args, **kwargs):
1. Try to dispatch to the right method; if a method doesn't exist,
1. defer to the error handler. Also defer to the error handler if the
1. 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)
翻译方法注释:
尝试分派到正确的方法;如果方法不存在,则转到错误处理程序。如果请求方法不在批准列表中,也会延迟到错误处理程序。
通过转换从view
传过来的request
的method
转换成字符串小写,然后判断是否在类属性下的self.http_method_names
列表里面。
分别设置handler
(处理器)
handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
和
handler = self.http_method_not_allowed
然后返回这个处理器给 view()
return handler(request, *args, **kwargs)
然后经过update_wrapper
把他变成一个wrapped function
(我们这里先暂时不讲什么是wrapped function
不过附带上 update_wrapper
的源码, 可以自己看一下
update_wrapper()
def update_wrapper(wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES):
"""Update a wrapper function to look like the wrapped function
wrapper is the function to be updated
wrapped is the original function
assigned is a tuple naming the attributes assigned directly
from the wrapped function to the wrapper function (defaults to
functools.WRAPPER_ASSIGNMENTS)
updated is a tuple naming the attributes of the wrapper that
are updated with the corresponding attribute from the wrapped
function (defaults to functools.WRAPPER_UPDATES)
"""
for attr in assigned:
try:
value = getattr(wrapped, attr)
except AttributeError:
pass
else:
setattr(wrapper, attr, value)
for attr in updated:
getattr(wrapper, attr).update(getattr(wrapped, attr, {}))
1. Issue #17482: set __wrapped__ last so we don't inadvertently copy it
1. from the wrapped function when updating __dict__
wrapper.__wrapped__ = wrapped
1. Return the wrapper so this can be used as a decorator via partial()
return wrapper
携带的四个参数
wrapper,
wrapped,
assigned = WRAPPER_ASSIGNMENTS,
updated = WRAPPER_UPDATES
分别代表的意思:
wrapper is the function to be updated # 要被更新的函数
wrapped is the original function # 原有的函数
assigned is a tuple naming the attributes assigned directly
from the wrapped function to the wrapper function (defaults to
functools.WRAPPER_ASSIGNMENTS) # 直接分发的属性(有默认值)
updated is a tuple naming the attributes of the wrapper that
are updated with the corresponding attribute from the wrapped
function (defaults to functools.WRAPPER_UPDATES)# 是一个元组,其中命名了包装器的属性,这些属性会用被包装函数的相应属性更新
return
最后的最后,返回 view
, 这个是已经被包装好的 view
函数名。这也是为什么我们一般在路由层里面写的as_view()()
要加两个括号,就是为了调用 base.py返回出来的这个 view
+()= 调用