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.
"""
# Go through keyword arguments, and either save their values to our
# 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
# 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

经过@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):
# 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)

翻译方法注释:

尝试分派到正确的方法;如果方法不存在,则转到错误处理程序。如果请求方法不在批准列表中,也会延迟到错误处理程序。

通过转换从view传过来的requestmethod转换成字符串小写,然后判断是否在类属性下的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, {}))
# Issue #17482: set __wrapped__ last so we don't inadvertently copy it
# from the wrapped function when updating __dict__
wrapper.__wrapped__ = wrapped
# 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+()= 调用