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
传过来的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, {})) # 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
+()= 调用