Django APIView源码解析 Django之 CBV和FBV

 APIView用法:

Django之 CBV和FBV中,我们是分析的from django.views import View下的执行流程,以下是代码

from django.views import  View
class IndexView(View):
    def get(self,request, *args, **kwargs):
        return HttpResponse("ok")

    def dispatch(self, request, *args, **kwargs):
        ret = super(IndexView,self).dispatch(request, *args, **kwargs)

        return  HttpResponse(ret)

这篇博客我们就来了解下APIView是如何执行的,跟django.views模块下的view有何关联? 
我们依然从url配置入手分析

url(r"books/$",views.BookView.as_view())
as_view方法代码如下

原来APIView类是继承View类,view类正式from django.views import View下的View,

先看as_view方法中的view = super(APIView, cls).as_view(**initkwargs)的这行代码, 
是调用了父类View中的as_view方法,这里的initkwargs,及其父类的View中的as_view方法执行流程,之类就不在赘述了

具体流程去看我博客开头的博客链接

 

所以在APIView类中,我们重点看下return csrf_exempt(view)做了什么操作?

def csrf_exempt(view_func):  
    def wrapped_view(*args, **kwargs):
        return view_func(*args, **kwargs)
    wrapped_view.csrf_exempt = True
    return wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)

wrapped_view.csrf_exempt = True意思是取消当前函数防跨站请求伪造功能,即便settings中设置了全局中间件,然后将csrf_exempt函数中的内置函数wrapped_view赋值wrapped_view.csrf_exempt = True,使其拥有该属性,

接下来看 wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)函数之前, 
我们先看下assigned=available_attrs(view_func)

def available_attrs(fn):
    if six.PY3:
        return WRAPPER_ASSIGNMENTS
    else:
        return tuple(a for a in WRAPPER_ASSIGNMENTS if hasattr(fn, a))

大概意思就是针对py3或者其他版本做了一些判断处理,最后通过WRAPPER_ASSIGNMENTS定为到

WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__qualname__', '__doc__',
                       '__annotations__')

这个逻辑跟我们上一篇的CBV源码有共同之处,就是为某个方法或者函数,添加某些属性 
接下来我们看wraps函数吧

def wraps(wrapped,
          assigned = WRAPPER_ASSIGNMENTS,
          updated = WRAPPER_UPDATES):
    return partial(update_wrapper, wrapped=wrapped,
                   assigned=assigned, updated=updated) 

wrapped是我们在def csrf_exempt(view_func):函数中的参数,也就是as_view的返回值,

最后通过functools模块下的partial类进行装饰构造,partial 这个东西是针对函数起作用的,并且是部分的, 
场景:有这样的函数:get_useragent(request) 用来获取用户浏览器的ua信息,但是这个函数又不是在主体函数(执行页面渲染的函数)get时调用的,只在模板中的一个filter中调用的(可以理解是在模板渲染时调用的),而filter在执行的时候是不能添加参数的,哪你要怎么处理。

这时partial就得闪亮登场,如下是代码,

def __new__(*args, **keywords):
        if not args:
            raise TypeError("descriptor '__new__' of partial needs an argument")
        if len(args) < 2:
            raise TypeError("type 'partial' takes at least one argument")
        cls, func, *args = args
        if not callable(func):
            raise TypeError("the first argument must be callable")
        args = tuple(args)

        if hasattr(func, "func"):
            args = func.args + args
            tmpkw = func.keywords.copy()
            tmpkw.update(keywords)
            keywords = tmpkw
            del tmpkw
            func = func.func

        self = super(partial, cls).__new__(cls)

        self.func = func
        self.args = args
        self.keywords = keywords
        return self

    def __call__(*args, **keywords):
        if not args:
            raise TypeError("descriptor '__call__' of partial needs an argument")
        self, *args = args
        newkeywords = self.keywords.copy()
        newkeywords.update(keywords)
        return self.func(*self.args, *args, **newkeywords)

最后在csrf_exempt函数中的wraps(view_func, assigned=available_attrs(view_func))(wrapped_view)这里写代码片传入参数wrapped_view,通过对象可调用功能,进行调用__call__方法 
到此as_view分析完毕,以上代码好多有迷惑的点,我分析的时候也是很多猜想的

上篇CBV源码分析中我们知道,当as_view执行后,当浏览器访问某个api接口时候, 
就会先触发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)

然而API方法中也有自己的dispatch方法,会执行子类的方法,而不是去执行父类的dispatch方法,以下是代码

    

以上dispatch方法中,通过self.initialize_request(request, *args, **kwargs)

 

新的request中

通过initialize_request进一步分装成自己的Request

然后self.initial(request, *args, **kwargs)

完善request请求的一些注意事项,例如用户登录、检测权限等等

然后response = handler(request, *args, **kwargs)这里面是执行了对应的请求操作,如get\post请求,也就是执行了我们自定义视图里面的get方法等,在处理完毕后,赋值response然后作为参数进行如下的操作

  

随后self.response = self.finalize_response(request, response, *args, **kwargs)返回,这一部操作,跟它的父类View是不同的, 
在继承APIView的视图中,get\post需要返回HttpResponse,然后在通过self.finalize_response进一步封装,最后返回

最后在dispatch

self.response = self.finalize_response(request, response, *args, **kwargs)
        return self.response 

猜你喜欢

转载自www.cnblogs.com/95lyj/p/9434221.html