Django 結構及處理流程分析

conf
主要有兩個做用:1) 處理全局配置, 好比數據庫、加載的應用、 MiddleWare等 2) 處理urls配置, 就是url與view的映射關係。
contrib (貢獻)
由Django的開發者貢獻的功能模塊,不過既然都已經隨版本發佈,就表示是官方的。
core
Django的核心處理庫,包括url分析、處理請求、緩存等,其中處理請求是核心了,好比處理fastcgi就是由wsgi.py處理。
db
顧名思義,處理與數據庫相關的,就是ORM。
dispatch (分派,派遣)
其實這不是Django原創,是pydispatch庫,主要處理消費者-工做者模式。
forms && newforms && oldforms
處理html的表單,不用多介紹。
middleware
中間件,就是處理HTTP的request和response的,相似插件。好比默認的common中間件的一個功能:當一個頁面沒有找對對應的 pattern時,會自定加上‘/’從新處理。好比訪問/blog時,而定義的pattern是‘^blog/$’,因此找不到對應的pattern,會自動再用/blog/查找,固然前提是 APPEND_SLASH=True。
template
Django的模板
templatetags
處理Application的tag的wrapper,就是將INSTALLED_APPS中全部的templatetags目錄添加到 django.templatetags目錄中,則當使用 {{load blog}}記載tag時,就可使用 import django.templatetags.blog 方式加載了。不過這有一個問題,若是其餘Application目錄中也有blog.py,這會加載第一個出現blog.py的tag。其實在 Django中,有許多須要處理重名的地方,好比template,須要格外當心,這個後續在介紹。
utils
公共庫,不少公用的類都在放在這裏。
views
最基本的view方法。

Django 術語

在應用 Django 的時候,咱們常常聽到一些術語:html

Project
指一個完整的Web服務,通常由多個模塊組成。
Application
能夠理解爲模塊,好比用戶管理、博客管理等,包含了數據的組成和數據的顯示,Applicaiton都須要在 "project/settings.py" 中  INSTALLED_APPS 的定義。
Middleware
就是處理request和response的插件, Middleware都須要在 "project/settings.py" 中 MIDDLEWARE_CLASSES 的定義。
Loader
模板加載器, 其實就是爲了讀取 Template 文件的類,默認的有經過文件系統加載和在 "Application/templates" 目錄中加載,Loader都須要在 "project/settings.py" 中  TEMPLATE_LOADERS 的定義。

處理流程

下面以 fastcgi 方式爲例,Django的版本是 "1.1" 。node

其實和其餘Web框架同樣,HTTP處理的流程大體相同(但和Tomcat等Java容器不相同),python

好比request->response.程序員

  1. 加載配置

    Django的配置都在 "Project/settings.py" 中定義,能夠是Django的配置,也能夠是自定義的配置,而且都經過 django.conf.settings 訪問,很是方便。正則表達式

  2. 啓動

    最核心動做的是經過 django.core.management.commands.runfcgi 的 Command 來啓動,它運行 django.core.servers.fastcgi 中的 runfastcgi , runfastcgi 使用了 flup 的 WSGIServer 來啓動 fastcgi 。而 WSGIServer 中攜帶了 django.core.handlers.wsgi 的 WSGIHandler 類的一個實例,經過 WSGIHandler來處理由Web服務器(好比Apache,Lighttpd等)傳過來的請求,此時纔是真正進入Django的世界。數據庫

  3. 處理 Request

    當有HTTP請求來時, WSGIHandler 就開始工做了,它從 BaseHandler 繼承而來。 WSGIHandler 爲每一個請求建立一個 WSGIRequest 實例,而 WSGIRequest 是從 http.HttpRequest 繼承而來。接下來就開始建立 Response 了.django

  4. 建立Response

    BaseHandler 的 get_response 方法就是根據 request 建立 response , 而具體生成 response 的動做就是執行 urls.py 中對應的view函數了,這也是 Django能夠處理「友好URL」的關鍵步驟,每一個這樣的函數都要返回一個 Response 實例。此時通常的作法是經過 loader 加載 template 並生成頁面內容,其中重要的就是經過 ORM 技術從數據庫中取出數據,並渲染到 Template 中,從而生成具體的頁面了數組

  5. 處理Response

    Django 返回 Response 給 flup , flup 就取出 Response 的內容返回給 Web 服務器,由後者返回給瀏覽器。瀏覽器

總之, Django 在 fastcgi 中主要作了兩件事:處理 Request 和建立 Response , 而它們對應的核心就是「urls分析」、「模板技術」和「ORM技術」,這些留在之後分析。附圖緩存

 
 

詳細全流程

一個 Request 到達了!

首先發生的是一些和 Django 有關(前期準備)的其餘事情,分別是:

  1. 若是是 Apache/mod_python 提供服務,request 由 mod_python 建立的 django.core.handlers.modpython.ModPythonHandler 實例傳遞給 Django。
  2. 若是是其餘服務器,則必須兼容 WSGI,這樣,服務器將建立一個 django.core.handlers.wsgi.WsgiHandler 實例。

這兩個類都繼承自 django.core.handlers.base.BaseHandler,它包含對任何類型的 request 來講都須要的公共代碼。

有一個處理器了

當上面其中一個處理器實例化後,緊接着發生了一系列的事情:

  1. 這個處理器導入你的 Django 配置文件。
  2. 這個處理器導入 Django 的自定義例外類。
  3. 這個處理器呼叫它本身的 load_middleware 方法,加載全部列在 MIDDLEWARE_CLASSES 中的 middleware 類而且內省它們。

最後一條有點複雜,咱們仔細瞧瞧。

一 個 middleware 類能夠滲入處理過程的四個階段:request,view,response 和 exception。要作到這一點,只須要定義指定的、恰當的方法:process_request,process_view, process_response 和 process_exception。middleware 能夠定義其中任何一個或全部這些方法,這取決於你想要它提供什麼樣的功能。

當處理器內省 middleware 時,它查找上述名字的方法,並創建四個列表做爲處理器的實例變量:

  1. _request_middleware 是一個保存 process_request 方法的列表(在每一種狀況下,它們是真正的方法,能夠直接呼叫),這些方法來自於任一個定義了它們的 middleware 類。
  2. _view_middleware 是一個保存 process_view 方法的列表,這些方法來自於任一個定義了它們的 middleware 類。
  3. _response_middleware 是一個保存 process_response 方法的列表,這些方法來自於任一個定義了它們的 middleware 類。
  4. _exception_middleware 是一個保存 process_exception 方法的列表,這些方法來自於任一個定義了它們的 middleware 類。

綠燈:如今開始

如今處理器已經準備好真正開始處理了,所以它給調度程序發送一個信號 request_started(Django 內部的調度程序容許各類不一樣的組件聲明它們正在幹什麼,並能夠寫一些代碼監聽特定的事件。關於這一點目前尚未官方的文檔,但在 wiki上有一些註釋。)。接下來它實例化一個 django.http.HttpRequest 的子類。根據不一樣的處理器,多是 django.core.handlers.modpython.ModPythonRequest 的一個實例,也多是 django.core.handlers.wsgi.WSGIRequest 的一個實例。須要兩個不一樣的類是由於 mod_python 和 WSGI APIs 以不一樣的格式傳入 request 信息,這個信息須要解析爲 Django 可以處理的一個單獨的標準格式。

一旦一個 HttpRequest 或者相似的東西存在了,處理器就呼叫它本身的 get_response 方法,傳入這個 HttpRequest 做爲惟一的參數。這裏就是幾乎全部真正的活動發生的地方。

Middleware,第一回合

get_response 作的第一件事就是遍歷處理器的 _request_middleware 實例變量並呼叫其中的每個方法,傳入 HttpRequest 的實例做爲參數。這些方法能夠選擇短路剩下的處理並當即讓 get_response 返回,經過返回自身的一個值(若是它們這樣作,返回值必須是 django.http.HttpResponse 的一個實例,後面會討論到)。若是其中之一這樣作了,咱們會當即回到主處理器代碼,get_response 不會等着看其它 middleware 類想要作什麼,它直接返回,而後處理器進入 response 階段。

然而,更通常的狀況是,這裏應用的 middleware 方法簡單地作一些處理並決定是否增長,刪除或補充 request 的屬性。

關於解析

假設沒有一個做用於 request 的 middleware 直接返回 response,處理器下一步會嘗試解析請求的 URL。它在配置文件中尋找一個叫作 ROOT_URLCONF 的配置,用這個配置加上根 URL /,做爲參數來建立 django.core.urlresolvers.RegexURLResolver 的一個實例,而後呼叫它的 resolve 方法來解析請求的 URL 路徑。

URL resolver 遵循一個至關簡單的模式。對於在 URL 配置文件中根據 ROOT_URLCONF 的配置產生的每個在 urlpatterns 列表中的條目,它會檢查請求的 URL 路徑是否與這個條目的正則表達式相匹配,若是是的話,有兩種選擇:

  1. 若是這個條目有一個能夠呼叫的 include,resolver 截取匹配的 URL,轉到 include 指定的 URL 配置文件並開始遍歷其中 urlpatterns 列表中的每個條目。根據你 URL 的深度和模塊性,這可能重複好幾回。
  2. 不然,resolver 返回三個條目:匹配的條目指定的 view function;一個從 URL 獲得的未命名匹配組(被用來做爲 view 的位置參數);一個關鍵字參數字典,它由從 URL 獲得的任意命名匹配組和從 URLConf 中獲得的任意其它關鍵字參數組合而成。

注意這一過程會在匹配到第一個指定了 view 的條目時中止,所以最好讓你的 URL 配置從複雜的正則過渡到簡單的正則,這樣能確保 resolver 不會首先匹配到簡單的那一個而返回錯誤的 view function。

若是沒有找到匹配的條目,resolver 會產生 django.core.urlresolvers.Resolver404 例外,它是 django.http.Http404 例外的子類。後面咱們會知道它是如何處理的。

Middleware,第二回合

一旦知道了所需的 view function 和相關的參數,處理器就會查看它的 _view_middleware 列表,並呼叫其中的方法,傳入 HttpRequst,view function,針對這個 view 的位置參數列表和關鍵字參數字典。

還有,middleware 有可能介入這一階段並強迫處理器當即返回。

進入 View

若是處理過程這時候還在繼續的話,處理器會呼叫 view function。Django 中的 Views 不很嚴格由於它只須要知足幾個條件:

  • 必須能夠被呼叫。
  • 必須接受 django.http.HttpRequest 的實例做爲第一位值參數。
  • 必須能產生一個例外或返回 django.http.HttpResponse 的一個實例。

除了這些,你就能夠天馬行空了。儘管如此,通常來講,views 會使用 Django 的 database API 來建立,檢索,更新和刪除數據庫的某些東西,還會加載並渲染一個模板來呈現一些東西給最終用戶。

模板

Django 的模板系統有兩個部分:一部分是給設計師使用的混入少許其它東西的 HTML,另外一部分是給程序員使用純 Python。

從一個 HTML 做者的角度,Django 的模板系統很是簡單,須要知道的僅有三個結構:

  1. 變量引用。在模板中是這樣: {{ foo }}。
  2. 模板過濾。在上面的例子中使用過濾豎線是這樣:{{ foo|bar }}。一般這用來格式化輸出(好比:運行 Textile,格式化日期等等)。
  3. 模板標籤。是這樣:{% baz %}。這是模板的「邏輯」實現的地方,你能夠 {% if foo %},{% for bar in foo %},等等,if 和 for 都是模板標籤。

變量引用以一種很是簡單的方式工做。若是你只是要打印變量,只要 {{ foo }},模板系統就會輸出它。這裏惟一的複雜狀況是 {{ foo.bar }},這時模板系統按順序嘗試幾件事:

  1. 首先它嘗試一個字典方式的查找,看看 foo['bar'] 是否存在。若是存在,則它的值被輸出,這個過程也隨之結束。
  2. 若是字典查找失敗,模板系統嘗試屬性查找,看看 foo.bar 是否存在。同時它還檢查這個屬性是否能夠被呼叫,若是能夠,呼叫之。
  3. 若是屬性查找失敗,模板系統嘗試把它做爲列表索引進行查找。

若是全部這些都失敗了,模板系統輸出配置 TEMPLATE_STRING_IF_INVALID 的值,默認是空字符串。

模板過濾就是簡單的 Python functions,它接受一個值和一個參數,返回一個新的值。好比,date 過濾用一個 Python datetime 對象做爲它的值,一個標準的 strftime 格式化字符串做爲它的參數,返回對 datetime 對象應用了格式化字符串以後的結果。

模板標籤用在事情有一點點複雜的地方,它是你瞭解 Django 的模板系統是如何真正工做的地方。

Django 模板的結構

在內部,一個 Django 模板體現爲一個 「nodes」 集合,它們都是從基本的 django.template.Node 類繼承而來。Nodes 能夠作各類處理,但有一個共同點:每個 Node 必須有一個叫作 render 的方法,它接受的第二個參數(第一個參數,顯然是 Node 實例)是 django.template.Context 的一個實例,這是一個相似於字典的對象,包含全部模板能夠得到的變量。Node 的 render 方法必須返回一個字符串,但若是 Node 的工做不是輸出(好比,它是要經過增長,刪除或修改傳入的 Context 實例變量中的變量來修改模板上下文),能夠返回空字符串。

Django 包含許多 Node 的子類來提供有用的功能。好比,每一個內置的模板標籤都被一個 Node 的子類處理(好比,IfNode 實現了 if 標籤,ForNode 實現了 for 標籤,等等)。全部內置標籤能夠在 django.template.defaulttags 找到。實際上,上面介紹的全部模板結構都是某種形式的 Nodes,純文本也不例外。變量查找由 VariableNode 處理,出於天然,過濾也應用在 VariableNode 上,標籤是各類類型的 Nodes,純文本是一個 TextNode。

通常來講,一個 view 渲染一個模板要通過下面的步驟,依次是:

  1. 加載須要渲染的模板。這是由 django.template.loader.get_template 完成的,它能利用這許多方法中的任意一個來定位須要的模板文件。 get_template 函數返回一個 django.template.Template 實例,其中包含通過解析的模板和用到的方法。
  2. 實例化一個 Context 用來渲染模板。若是用的是 Context 的子類 django.template.RequestContext,那麼附帶的上下文處理函數就會自動添加在 view 中沒有定義的變量。Context 的構建器方法用一個鍵/值對的字典(對於模板,它將變爲名/值變量)做爲它惟一的參數,RequestContext 則用 HttpRequest 的一個實例和一個字典。
  3. 呼叫 Template 實例的 render 方法,Context 對象做爲第一個位置參數。

Template 的 render 方法的返回值是一個字符串,它由 Template 中全部 Nodes 的 render 方法返回的值鏈接而成,呼叫順序爲它們出如今 Template 中的順序。

關於 Response,一點點

一旦一個模板完成渲染,或者產生了其它某些合適的輸出,view 就會負責產生一個 django.http.HttpResponse 實例,它的構建器接受兩個可選的參數:

  1. 一個做爲 response 主體的字符串(它應該是第一位置參數,或者是關鍵字參數 content)。大部分時間,這將做爲渲染一個模板的輸出,但不是必須這樣,在這裏你能夠傳入任何有效的 Python 字符串。
  2. 做 爲 response 的 Content-Type header 的值(它應該是第二位置參數,或者是關鍵字參數 mine_type)。若是沒有提供這個參數,Django 將會使用配置中 DEFAULT_MIME_TYPE 的值和 DEFAULT_CHARSET 的值,若是你沒有在 Django 的全局配置文件中更改它們的話,分別是 「text/html」 和 「utf-8」。

Middleware,第三回合:例外

若是 view 函數,或者其中的什麼東西,發生了例外,那麼 get_response(我知道咱們已經花了些時間深刻 views 和 templates,可是一旦 view 返回或產生例外,咱們仍將重拾處理器中間的 get_response 方法)將遍歷它的 _exception_middleware 實例變量並呼叫那裏的每一個方法,傳入 HttpResponse 和這個 exception 做爲參數。若是順利,這些方法中的一個會實例化一個 HttpResponse 並返回它。

仍然沒有響應?

這時候有可能仍是沒有獲得一個 HttpResponse,這可能有幾個緣由:

  1. view 可能沒有返回值。
  2. view 可能產生了例外但沒有一個 middleware 能處理它。
  3. 一個 middleware 方法試圖處理一個例外時本身又產生了一個新的例外。

這時候,get_response 會回到本身的例外處理機制中,它們有幾個層次:

  1. 若是 exception 是 Http404 而且 DEBUG 設置爲 True,get_response 將執行 view django.views.debug.technical_404_response,傳入 HttpRequest 和 exception 做爲參數。這個 view 會展現 URL resolver 試圖匹配的模式信息。
  2. 若是 DEBUG 是 False 而且例外是 Http404,get_response 會呼叫 URL resolver 的 resolve_404 方法。這個方法查看 URL 配置以判斷哪個 view 被指定用來處理 404 錯誤。默認是 django.views.defaults.page_not_found,但能夠在 URL 配置中給 handler404 變量賦值來更改。
  3. 對於任何其它類型的例外,若是 DEBUG 設置爲 True,get_response 將執行 view django.views.debug.technical_500_response,傳入 HttpRequest 和 exception 做爲參數。這個 view 提供了關於例外的詳細信息,包括 traceback,每個層次 stack 中的本地變量,HttpRequest 對象的詳細描述和全部無效配置的列表。
  4. 若是 DEBUG 是 False,get_response 會呼叫 URL resolver 的 resolve_500 方法,它和 resolve_404 方法很是類似,這時默認的 view 是 django.views.defaults.server_error,但能夠在 URL 配置中給 handler500 變量賦值來更改。

此外,對於除了 django.http.Http404 或 Python 內置的 SystemExit 以外的任何例外,處理器會給調度者發送信號 got_request_exception,在返回以前,構建一個關於例外的描述,把它發送給列在 Django 配置文件的 ADMINS 配置中的每個人。

Middleware,最後回合

如今,不管 get_response 在哪個層次上發生錯誤,它都會返回一個 HttpResponse 實例,所以咱們回處處理器的主要部分。一旦它得到一個 HttpResponse 它作的第一件事就是遍歷它的 _response_middleware 實例變量並應用那裏的方法,傳入 HttpRequest 和 HttpResponse 做爲參數。

注意對於任何想改變點什麼的 middleware 來講,這是它們的最後機會。

The check is in the mail

是該結束的時候了。一旦 middleware 完成了最後環節,處理器將給調度者發送信號 request_finished,對與想在當前的 request 中執行的任何東西來講,這絕對是最後的呼叫。監聽這個信號的處理者會清空並釋聽任何使用中的資源。好比,Django 的 request_finished 監聽者會關閉全部數據庫鏈接。

這件事發生之後,處理器會構建一個合適的返回值送返給實例化它的任何東西(如今,是一個恰當的 mod_python response 或者一個 WSGI 兼容的 response,這取決於處理器)並返回。

呼呼

結束了,從開始到結束,這就是 Django 如何處理一個 request。

相關文章
相關標籤/搜索