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

url(r"books/$",views.BookView.as_view())

as_view方法代码如下

@classmethod
    def as_view(cls, **initkwargs):
        """
        Store the original class on the view function.

        This allows us to discover information about the view when we do URL
        reverse lookups.  Used for breadcrumb generation.
        """
        if isinstance(getattr(cls, 'queryset', None), models.query.QuerySet):
            def force_evaluation():
                raise RuntimeError(
                    'Do not evaluate the `.queryset` attribute directly, '
                    'as the result will be cached and reused between requests. '
                    'Use `.all()` or call `.get_queryset()` instead.'
                )
            cls.queryset._fetch_all = force_evaluation

        view = super(APIView, cls).as_view(**initkwargs)
        view.cls = cls
        view.initkwargs = initkwargs

        # Note: session based authentication is explicitly CSRF validated,
        # all other authentication is CSRF exempt.
        return csrf_exempt(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方法执行流程,之类就不在赘述了,简单说就是在如下IndexView类的执行流程就是

先去执行print("dispatch")–>然后在去执行print("get")方法–>然后在去执行super(IndexView,self).dispatch(request, *args, **kwargs)–>最后执行return HttpResponse(ret) 
具体流程去看我这票博客开头的博客链接

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

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

 

所以在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)

 

点击partial跟进

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)

 

去通过handler(request, *args, **kwargs)执行requet对应的请求方法,诸如get\post等,然后在执行完毕后统一返回return handler(request, *args, **kwargs)最后在dispatch我们编写的试图函数中返回return HttpResponse(ret)即可 
在我们编写的视图函数中,这里的ret就是ret = super(IndexView,self).dispatch(request, *args, **kwargs)这是上篇博客的逻辑,本片博客也是适应的,因为APIView继承自View

那么接下来看下我们本片博客,也就是继承APIView的编写的视图

class BookView(APIView):

    def get(self,request,*args,**kwargs):

        book_list = Book.objects.all()

        # 当我们输入参数many = True时, serializer还能序列化queryset
        bs = BookSerializers(book_list, many=True)
        # print(bs.data)  # 序列化的结果
        return Response(bs.data)

 

posted on 2018-04-08 21:35  Py行僧  阅读(190)  评论(0编辑  收藏  举报