1. 程式人生 > >Django APIView源碼解析

Django APIView源碼解析

mes att 了解 san ini ans ati ffffff 執行流程

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 

Django APIView源碼解析