1. 程式人生 > >django rest_fremework源碼流程總體剖析

django rest_fremework源碼流程總體剖析

依次 又是 ted mat trie cit self. post請求 col

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         return
HttpResponse(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源碼流程總體剖析