django rest_fremework源碼流程總體剖析
1. 序言
有如下django代碼,視圖層:
1 from django.http import HttpResponse 2 from rest_framework.views import APIView 3 class OrdersView(APIView): 4 def get(self , request , *args , **kwargs): 5 return HttpResponse(‘GET請求‘) 6 def post(self , request , *args , **kwargs): 7 returnHttpResponse(‘POST請求‘) 8 def put(self , request , *args , **kwargs): 9 return HttpResponse(‘PUT請求‘) 10 def delete(self , request , *args , **kwargs): 11 return HttpResponse(‘DELETE請求‘)
路由配置為:
1 urlpatterns = [ 2 …… 3 url(r‘^orders/‘, OrdersView.as_view()), 4]
那麽,可以在瀏覽器中通過鏈接http://127.0.0.1:8000/orders/進行訪問,可以看到,瀏覽器頁面中輸出了“GET請求”的字樣。說明OrdersView類中的get方法被調用了。那麽,從瀏覽器中通過“http://127.0.0.1:8000/orders/”發出請求到服務器返回response經歷了怎麽樣的一個過程呢?get方法又是如何被調用的呢?
2. As_view方法
當鏈接“http://127.0.0.1/8000/orders/”的request請求發到服務器後,通過urls.py中的路由匹配到“url(r‘^orders/‘, OrdersView.as_view())”這一項。有一點必須明確的是,一個url配置項就對應一個方法,FBV(基於函數的試圖)如此,CBV(基於類的試圖)。所以,“url(r‘^orders/‘, OrdersView.as_view())”對應的最直觀上來說就是OrdersView類中的as_view方法,也就是說,當這個url被匹配到後,OrdersView的as_view立即被調用。
但我們在OrdersView中並沒有定義as_view方法呀,那as_view會在哪呢?答:在父類中。從定義OrdersView類代碼可以看出,OrdersView繼承類rest_framework.views中的 APIView類,打開APIView就可以找到as_view的源碼,代碼如下:
1 def as_view(cls, **initkwargs): 2 if isinstance(getattr(cls, ‘queryset‘, None), models.query.QuerySet): 3 def force_evaluation(): 4 raise RuntimeError( 5 ‘Do not evaluate the `.queryset` attribute directly, ‘ 6 ‘as the result will be cached and reused between requests. ‘ 7 ‘Use `.all()` or call `.get_queryset()` instead.‘ 8 ) 9 10 cls.queryset._fetch_all = force_evaluation 11 #繼續調用父類中的as_view方法 12 view = super(APIView, cls).as_view(**initkwargs) 13 view.cls = cls 14 view.initkwargs = initkwargs 15 # Note: session based authentication is explicitly CSRF validated, 16 # all other authentication is CSRF exempt. 17 return csrf_exempt(view)
可以看出,APIView中的代碼除去提示信息和註釋就沒有多少了,為什麽呢?因為它將大部分的工作都交給了父類的as_view去做。代碼中有一行“view = super(APIView, cls).as_view(**initkwargs)”,也就是通過super關鍵字繼續調用父類as_view。APIView的父類是django自帶的View類 在django.views.generic模塊中,我們找到其中的as_view方法,源碼如下所示:
1 def as_view(cls, **initkwargs): 2 """Main entry point for a request-response process.""" 3 for key in initkwargs: 4 if key in cls.http_method_names: 5 raise TypeError("You tried to pass in the %s method name as a " 6 "keyword argument to %s(). Don‘t do that." 7 % (key, cls.__name__)) 8 if not hasattr(cls, key): 9 raise TypeError("%s() received an invalid keyword %r. as_view " 10 "only accepts arguments that are already " 11 "attributes of the class." % (cls.__name__, key)) 12 def view(request, *args, **kwargs): 13 #用cls關鍵字實例化之類,也就是OrderViews 14 #這行代碼相當於self = OrdersView(**initkwargs) 15 #所以之後的self就是OrdersView實例 16 self = cls(**initkwargs) 17 if hasattr(self, ‘get‘) and not hasattr(self, ‘head‘): 18 self.head = self.get 19 self.request = request 20 self.args = args 21 self.kwargs = kwargs 22 #這才是最關鍵的地方:調用dispatch方法 23 return self.dispatch(request, *args, **kwargs) 24 view.view_class = cls 25 view.view_initkwargs = initkwargs 26 update_wrapper(view, cls, updated=()) 27 update_wrapper(view, cls.dispatch, assigned=()) 28 return view
這個as_view代碼也還是不多,主要就是在as_view方法內部定義了一個view方法,然後返回這個方法。但最關鍵的是,在這個as_view中調用了一個dispatch方法,大部分的工作都是在這個dispatch中完成的。
3. dispatch方法
dispatch在哪呢?Python會從調用處開始找(也就是OrdersView中開始找,可不是就近原則在View中開始找dispatch,雖然該類中確實有一個dispatch方法)。OrdersView中沒有dispatch方法,接下來繼續在OrdersView的父類(APIViews類)中尋找,果然就找到了,源碼如下:
1 def dispatch(self, request, *args, **kwargs): 2 self.args = args 3 self.kwargs = kwargs 4 #第一步:對從瀏覽器端傳過來的原生request進行包裝加工 5 request = self.initialize_request(request, *args, **kwargs) 6 self.request = request 7 self.headers = self.default_response_headers # deprecate? 8 try: 9 #第二步:進行一下操作 10 # 1.處理版權信息 11 # 2.認證 12 # 3.權限 13 # 4.請求用戶進行訪問頻率的限制 14 self.initial(request, *args, **kwargs) 15 # Get the appropriate handler method 16 #第三步:通過反射獲取請求方法(get、post、put…),並調用 17 #request.method的值(GET、POST…)是大寫的,所以要用變成小寫 18 if request.method.lower() in self.http_method_names: 19 #這個時候handler的值就是get、post、put等 20 handler = getattr(self, request.method.lower(), 21 self.http_method_not_allowed) 22 else: 23 handler = self.http_method_not_allowed 24 #調用請求方法(get、post…) 25 response = handler(request, *args, **kwargs) 26 except Exception as exc: 27 response = self.handle_exception(exc) 28 self.response = self.finalize_response(request, response, *args, **kwargs) 29 return self.response
dispatch方法是最核心的一個方法,在這個方法執行主要包括三個過程:
第一步:對從瀏覽器端傳過來的原生request進行包裝加工。
第二步:進行處理版權信息、認證、權限、請求用戶進行訪問頻率的限制。
第三步:通過反射獲取請求方法(get、post、put…),並調用。
下面繼續結合源代碼對上述三個步驟進行剖析。
第一個步驟是通過調用initialize_request方法來完成的,initialize_request方法也是在APIView中,源碼如下:
1 def initialize_request(self, request, *args, **kwargs): 2 # 把請求轉化成一個字典 3 parser_context = self.get_parser_context(request) 4 return Request( 5 request,#原生的request 6 parsers=self.get_parsers(),#解析數據 7 authenticators=self.get_authenticators(),#認證 8 negotiator=self.get_content_negotiator(), 9 parser_context=parser_context 10 )
所以,在第一步中,initialize_request方法在原生的request的基礎上添加了一些新的參數進去,包括數據解析實例、認證實例等,那麽是怎麽添加的呢?以authenticators(認證)為例進行說明。找到get_authenticators方法,源碼如下:
1 def get_authenticators(self): 2 #authentication_classes是類名列表,所以auth就是類名,auth()就是類實例 3 return [auth() for auth in self.authentication_classes]
在get_authenticators方法中,以列表解析式的方式生成了一個包含所有認證類實例的列表。
第二步操作中進行處理版權信息、認證、權限、請求用戶進行訪問頻率的限制,在initial方法中進行。Initial源碼如下:
1 def initial(self, request, *args, **kwargs): 2 """ 3 在調用方法處理程序之前運行需要發生的任何事情(例如:認證、權限、訪問頻率控制)。 4 """ 5 self.format_kwarg = self.get_format_suffix(**kwargs) 6 neg = self.perform_content_negotiation(request) 7 request.accepted_renderer, request.accepted_media_type = neg 8 version, scheme = self.determine_version(request, *args, **kwargs) 9 request.version, request.versioning_scheme = version, scheme 10 self.perform_authentication(request)#執行認證 11 self.check_permissions(request)#檢查權限 12 self.check_throttles(request)#頻率控制 13 #上述3種操作,如果都通過,那麽返回None,如果不通過則拋出異常
在initial方法中並沒有返回值,因為如果認證、權限、頻率控制都通過了,那程序繼續執行第三步。如果不通過,那麽拋出異常,第三步也不會執行了。
第三步就是執行get、post等請求方法了。這一步的操作關鍵代碼都是在dispatch本類中進行,判斷是哪種請求方法並不是通過類似於request.method==”get”,而是通過反射的方式,在上述dispatch源碼中我做了註釋,看源碼就好了。
執行請求方法(get、post…)時,還是優先調用OrdersView類中的方法,沒有在去父類中找。本例中因為OrdersView中定義了get方法,所以會執行自定制的get方法,返回HttpResponse實例。
至此,所有關鍵操作已經完成,剩下都工作就是就得到請求方法中生成的HttpResponse實例依次向上返回。
django rest_fremework源碼流程總體剖析