DRF | rest_framework 基层 Views 源码分析

2023年 9月 14日 56.2k 0

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+()= 调用

相关文章

JavaScript2024新功能:Object.groupBy、正则表达式v标志
PHP trim 函数对多字节字符的使用和限制
新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
为React 19做准备:WordPress 6.6用户指南
如何删除WordPress中的所有评论

发布评论