Django APIView源碼解析
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源碼解析