您現在的位置是:網站首頁> 內容頁

源碼解析Django CBV的本質

  • 宏威國際手機客戶端登錄
  • 2019-08-20
  • 104人已閱讀
簡介DjangoCBV模式的源碼解析通常來說,http請求的本質就是基于SocketDjango的視圖函數,可以基于FBV模式,也可以基于CBV模式。基于FBV的模式就是在Django的

Django CBV模式的源碼解析

通常來說,http請求的本質就是基于Socket

Django的視圖函數,可以基于FBV模式,也可以基于CBV模式。

基于FBV的模式就是在Django的路由映射表里進行url和視圖函數的關聯,而基于CBV的模式則是在views.py文件中定義視圖類,在視圖類中視圖函數,如get,post,put,delete等

使用Django新建一個項目,新建一個路由映射

from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [ url(r"^cbv/$",views.CBV.as_view())]

對應的views.py文件內容:

from django.shortcuts import render,HttpResponsefrom django.views import Viewclass CBV(View): def get(self,request): return HttpResponse("GET") def post(self,request): return HttpResponse("POST")

啟動項目,使用瀏覽器請求URLhttp://127.0.0.1:8000/cbv/,瀏覽器顯示結果為:

請求到達Django會先執行Django中間件里的方法,然后進行進行路由匹配。

在路由匹配完成后,會執行CBV類中的as_view方法。

CBV中并沒有定義as_view方法,由于CBV繼承自Django的View,所以會執行Django的View類中的as_view方法

Django的View類的as_view方法的部分源碼

class View(object): """ Intentionally simple parent class for all views. Only implements dispatch-by-method and simple sanity checking. """ http_method_names = ["get", "post", "put", "patch", "delete", "head", "options", "trace"] 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 six.iteritems(kwargs): setattr(self, key, value) @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("You tried to pass in the %s method name as a " "keyword argument to %s(). Don"t do that." % (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) if hasattr(self, "get") and not hasattr(self, "head"): self.head = self.get self.request = request self.args = args self.kwargs = kwargs 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

從View的源碼可以看出,在View類中,先定義了http請求的八種方法

http_method_names = ["get", "post", "put", "patch", "delete", "head", "options", "trace"]

as_view方法中進行判斷,如果請求的方法沒在http_method_names中,則會拋出異常,這里的cls實際上指的是自定義的CBV類

接著as_view方法中又定義view方法,在view方法中對CBV類進行實例化,得到self對象,然后在self對象中封裝瀏覽器發送的request請求

self = cls(**initkwargs)

最后又調用了self對象中的dispatch方法并返回dispatch方法的值來對request進行處理

此時,由于self對象就是CBV實例化得到,所以會先執行自定義的CBV類中的dispatch方法。如果CBV類中沒有定義dispatch方法則執行Django的View中的dispatch方法

Django的View中的dispatch方法源碼

class View(object): """ 中間省略 """ 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)

在dispatch方法中,把request.method轉換為小寫再判斷是否在定義的http_method_names中,如果request.method存在于http_method_names中,則使用getattr反射的方式來得到handler

在這里的dispatch方法中,self指的是自定義的CBV類實例化得到的對象

從CBV類中獲取request.method對應的方法,再執行CBV中的方法并返回

由此,可以知道如果在Django項目中使用CBV的模式,實際上調用了getattr的方式來執行獲取類中的請求方法對應的函數

結論:

CBV基于反射實現根據請求方式不同,執行不同的方法

自定義dispatch方法

如果想在基于CBV模式的項目中在請求某個url時執行一些操作,則可以在url對應的類中定義dispatch方法

修改views.py文件

class CBV(View): def dispatch(self, request, *args, **kwargs): func = getattr(self,request.method.lower()) return func(request,*args,**kwargs) def get(self,request): return HttpResponse("GET") def post(self,request): return HttpResponse("POST")

也可以使用繼承的方式重寫dispatch方法:

class CBV(View): def dispatch(self, request, *args, **kwargs): print("before") res = super(CBV, self).dispatch(request, *args, **kwargs) print("after") return res def get(self,request): return HttpResponse("GET") def post(self,request): return HttpResponse("POST")

刷新瀏覽器,Django后臺打印結果如下:

瀏覽器頁面結果

同理,如果有基于CBV的多個類,并且有多個類共用的功能,為了避免重復,可以單獨定義一個類,在這個類中重寫dispatch方法,然后讓url對應的視圖類繼承這個類

修改urls.py文件

from django.conf.urls import urlfrom django.contrib import adminfrom app01 import viewsurlpatterns = [ url(r"^cbv1/$",views.CBV1.as_view()), url(r"^cbv2/$",views.CBV2.as_view()),]

views.py文件內容

from django.shortcuts import render,HttpResponsefrom django.views import Viewclass BaseView(object): def dispatch(self, request, *args, **kwargs): func = getattr(self, request.method.lower()) return func(request, *args, **kwargs)class CBV1(BaseView,View): def get(self,request): return HttpResponse("CBV1 GET") def post(self,request): return HttpResponse("CBV1 POST")class CBV2(BaseView,View): def get(self,request): return HttpResponse("CBV2 GET") def post(self,request): return HttpResponse("CBV2 POST")

通過python的面向對象可以知道,請求到達視圖類時,會先執行CBV1和CBV2類中的dispatch方法,然而CBV1和CBV2類中并沒有dispatch方法,則會按照順序在父類中查找dispatch方法,此時就會執行BaseView類中的dispatch方法了

用瀏覽器請求urlhttp://127.0.0.1:8000/cbv1/,瀏覽器頁面顯示

用瀏覽器請求urlhttp://127.0.0.1:8000/cbv2/,瀏覽器頁面顯示

文章評論

Top 黑龙江快乐十分走势