1. 程式人生 > 其它 >17Django CBV基類View原始碼解析

17Django CBV基類View原始碼解析

前述章節《Django的FBV與CBV模式》中我們講解了 Django 中編寫檢視層函式的兩種方式,一種是基於函式即 FBV,另外一種是 CBV 即基於類的檢視函式。在本節,我們對類檢視中所繼承的 View 原始碼進一步分析,幫助大家更好的理解類檢視。若以後在專案中使用它就會更加得心應手。

View 定義於 django/views/generic/base.py 檔案中,其功能實現主要依賴於三個重要的方法分別如下所示:

  • dispatch
  • as_view
  • http_method_not_allowed

下面我們根據原始碼依次對這個三個方法進行分析。

1. http_method_not_allowed方法

這個方法返回 HttpResponseNotAllowed(405)響應,屬於繼承 HttpResponse 的子類,表示當前的請求型別不被支援。它在 View 類中的原始碼如下所示:

  1. class View:
  2. def http_method_not_allowed(self, request, *args, **kwargs):
  3. logger.warning(
  4. 'Method Not Allowed (%s): %s', request.method, request.path,
  5. extra={'status_code': 405, 'request': request}
  6. )
  7. return HttpResponseNotAllowed(self._allowed_methods())

比如當一個類檢視函式之定義了 get 方法,而沒有定義 post 方法,那麼這個類檢視函式接收到 post 的請求的時候,由於找不到相對應的 post 的定義,就會通過另一個方法 dispatch 分發到 http_method_not_allowed 方法中。

2. dispatch方法分析

dispatch 方法根據 HTTP 請求型別呼叫 View 中的同名函式,實現了請求的分發,原始碼如下所示:

  1. class View:
  2. def dispatch(self, request, *args, **kwargs):
  3. if request.method.lower() in self.http_method_names:
  4. handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
  5. else:
  6. handler = self.http_method_not_allowed
  7. return handler(request, *args, **kwargs)

http_method_names 定義當前 View可以接受的請求型別:

  1. http_method_names = ['get', 'post', 'put', 'patch', 'delete', 'head', 'options', 'trace']

首先,if 判斷當前的請求型別是否可以被接受,即請求方是否定義在 http_method_names 變數中。若能夠接受請求,則 dispatch() 嘗試獲取 View 中的同名方法,如果不存在,則將 handler 指定為 http_method_not_allowed 即不被允許的方法。然後 else 規定如果方法不被接受則直接將其指定為 http_method_not_allowed。

3. as_view方法分析

Django 給 as_view 方法加了@classonlymethod裝飾器,作用是隻允許類物件呼叫這個方法,如果是類例項呼叫,將會丟擲 AttributeError 異常。該方法的原始碼如下所示:

純文字複製
  1. class View:
  2. @classonlymethod
  3. def as_view(cls, **initkwargs):
  4. for key in initkwargs:
  5. if key in cls.http_method_names:
  6. raise TypeError("You tried to pass in the %s method name as a "
  7. "keyword argument to %s(). Don't do that."
  8. % (key, cls.__name__))
  9. if not hasattr(cls, key):
  10. raise TypeError("%s() received an invalid keyword %r. as_view "
  11. "only accepts arguments that are already "
  12. "attributes of the class." % (cls.__name__, key))
  13. def view(request, *args, **kwargs):
  14. #建立View類例項
  15. self = cls(**initkwargs)
  16. if hasattr(self, 'get') and not hasattr(self, 'head'):
  17. self.head = self.get
  18. self.setup(request, *args, **kwargs)
  19. if not hasattr(self, 'request'):
  20. raise AttributeError(
  21. "%s instance has no 'request' attribute. Did you override "
  22. "setup() and forget to call super()?" % cls.__name__
  23. )
  24. #呼叫View例項的dispatch方法
  25. return self.dispatch(request, *args, **kwargs)
  26. view.view_class = cls
  27. view.view_initkwargs = initkwargs
  28. update_wrapper(view, cls, updated=())
  29. update_wrapper(view, cls.dispatch, assigned=())
  30. return view

Django 將一個 HTTP 請求對映到一個可呼叫的函式,而不是一個類物件。所以,在定義 URL 路由的時候總是需要呼叫 View 的 as_view 方法。可以看到,上述的as_view 方法,它建立了 View 類的例項,然後呼叫了 dispatch 方法,根據請求型別分發處理請求函式。

在本節我們通過介紹 View 的原始碼,詳細解析了基於類檢視的實現原理,當一個請求進來的時候,檢視層是如何讓處理的,分別呼叫了哪些方法,最終實現了請求的分發處理。