1. 程式人生 > >django從請求到返回都經歷了什麽[轉]

django從請求到返回都經歷了什麽[轉]

admin indicate reg 幫助 頁面 prev manager setting roc

原文地址:http://projectsedu.com/2016/10/17/django從請求到返回都經歷了什麽/

從runserver說起
ruserver是使用django自己的web server,主要用於開發和調試中, 部署到線上環境一般使用nginx+uwsgi模式
manage.py 探秘
看一下manager.py的源碼,你會發現上面的命令其實是通過Django的execute_from_command_line方法執行了內部實現的runserver命令,那麽現在看一下runserver具體做了什麽。。
通過源碼分析可知, ruserserver主要完成兩件事:

1). 解析參數,並通過django.core.servers.basehttp.get_internal_wsgi_application方法獲取wsgi handler;
2). 根據ip_address和port生成一個WSGIServer對象,接受用戶請求

get_internal_wsgi_application的源碼如下:

技術分享

通過上面的代碼我們可以知道,Django會先根據settings中的WSGI_APPLICATION來獲取handler;
在創建project的時候,Django會默認創建一個wsgi.py文件,而settings中的WSGI_APPLICATION配置也會默認指向這個文件。看一下這個wsgi.py文件,其實它也和上面的邏輯一樣,最終調用get_wsgi_application實現。
django http請求處理流程
Django和其他Web框架一樣,HTTP的處理流程基本類似:接受request,返回response內容。Django的具體處理流程大致如下:
1. 加載settings.py
在通過django-admin.py創建project的時候,Django會自動生成默認的settings文件和manager.py等文件,在創建WSGIServer之前會執行下面的引用:

from django.conf import settings

上面引用在執行時,會讀取os.environ中的DJANGO_SETTINGS_MODULE配置,加載項目配置文件,生成settings對象。所以,在manager.py文件中你可以看到,在獲取WSGIServer之前,會先將project的settings路徑加到os路徑中。
2. 創建WSGIServer
不管是使用runserver還是uWSGI運行Django項目,在啟動時都會調用django.core.servers.basehttp中的run()方法
創建一個django.core.servers.basehttp.WSGIServer類的實例,之後調用其serve_forever()方法啟動HTTP服務。run方法的源碼如下:

def run(addr, port, wsgi_handler, ipv6=False, threading=False):
    server_address = (addr, port)
    if threading:
        httpd_cls = type(str(‘WSGIServer‘), (socketserver.ThreadingMixIn, WSGIServer), {})
    else:
        httpd_cls = WSGIServer
    httpd = httpd_cls(server_address, WSGIRequestHandler, ipv6=ipv6)
    if threading:
        # ThreadingMixIn.daemon_threads indicates how threads will behave on an
        # abrupt shutdown; like quitting the server by the user or restarting
        # by the auto-reloader. True means the server will not wait for thread
        # termination before it quits. This will make auto-reloader faster
        # and will prevent the need to kill the server manually if a thread
        # isn‘t terminating correctly.
        httpd.daemon_threads = True
    httpd.set_app(wsgi_handler)
    httpd.serve_forever()

如上,我們可以看到:在創建WSGIServer實例的時候會指定HTTP請求的Handler,
上述代碼使用WSGIRequestHandler。當用戶的HTTP請求到達服務器時,
WSGIServer會創建WSGIRequestHandler實例,使用其handler方法來處理HTTP請求(其實最終是調用wsgiref.handlers.BaseHandler中的run方法處理)。
WSGIServer通過set_app方法設置一個可調用(callable)的對象作為application,上面提到的handler方法最終會調用設置的application處理request,並返回response。

其中,WSGIServer繼承自wsgiref.simple_server.WSGIServer,而WSGIRequestHandler繼承自wsgiref.simple_server.WSGIRequestHandler,wsgiref是Python標準庫給出的WSGI的參考實現。其源碼可自行到wsgiref參看,這裏不再細說。
3. 處理Request
第二步中說到的application,在Django中一般是django.core.handlers.wsgi.WSGIHandler對象,WSGIHandler繼承自django.core.handlers.base.BaseHandler,這個是Django處理request的核心邏輯,它會創建一個WSGIRequest實例,而WSGIRequest是從http.HttpRequest繼承而來
4. 返回Response
上面提到的BaseHandler中有個get_response方法,該方法會先加載Django項目的ROOT_URLCONF,然後根據url規則找到對應的view方法(類),view邏輯會根據request實例生成並返回具體的response。

在Django返回結果之後,第二步中提到wsgiref.handlers.BaseHandler.run方法會調用finish_response結束請求,並將內容返回給用戶
Django處理Request的詳細流程
首先給大家分享兩個網上看到的Django流程圖:

技術分享
流程圖一

技術分享
流程圖二

上面的兩張流程圖可以大致描述Django處理request的流程,按照流程圖2的標註,可以分為以下幾個步驟:

1. 用戶通過瀏覽器請求一個頁面

2.請求到達Request Middlewares,中間件對request做一些預處理或者直接response請求
3.URLConf通過urls.py文件和請求的URL找到相應的View
4.View Middlewares被訪問,它同樣可以對request做一些處理或者直接返回response
5.調用View中的函數
6.View中的方法可以選擇性的通過Models訪問底層的數據
7.所有的Model-to-DB的交互都是通過manager完成的
8.如果需要,Views可以使用一個特殊的Context
9.Context被傳給Template用來生成頁面
    a.Template使用Filters和Tags去渲染輸出
    b.輸出被返回到View
    c.HTTPResponse被發送到Response Middlewares
    d.任何Response Middlewares都可以豐富response或者返回一個完全不同的response
    e.Response返回到瀏覽器,呈現給用戶

上述流程中最主要的幾個部分分別是:Middleware(中間件,包括request, view, exception, response),URLConf(url映射關系),Template(模板系統),下面一一介紹一下。
1. Middleware(中間件)
Middleware並不是Django所獨有的東西,在其他的Web框架中也有這種概念。在Django中,Middleware可以滲入處理流程的四個階段:request,view,response和exception,相應的,在每個Middleware類中都有rocess_request,process_view, process_response 和 process_exception這四個方法。你可以定義其中任意一個活多個方法,這取決於你希望該Middleware作用於哪個處理階段。每個方法都可以直接返回response對象。

Middleware是在Django BaseHandler的load_middleware方法執行時加載的,加載之後會建立四個列表作為處理器的實例變量:

_request_middleware:process_request方法的列表

_view_middleware:process_view方法的列表

_response_middleware:process_response方法的列表

_exception_middleware:process_exception方法的列表

Django的中間件是在其配置文件(settings.py)的MIDDLEWARE_CLASSES元組中定義的。在MIDDLEWARE_CLASSES中,中間件組件用字符串表示:指向中間件類名的完整Python路徑。例如

`MIDDLEWARE_CLASSES = [
    ‘django.middleware.security.SecurityMiddleware‘,
    ‘django.contrib.sessions.middleware.SessionMiddleware‘,
    ‘django.middleware.common.CommonMiddleware‘,
    ‘django.middleware.csrf.CsrfViewMiddleware‘,
    ‘django.contrib.auth.middleware.AuthenticationMiddleware‘,
    ‘django.contrib.auth.middleware.SessionAuthenticationMiddleware‘,
    ‘django.contrib.messages.middleware.MessageMiddleware‘,
    ‘django.middleware.clickjacking.XFrameOptionsMiddleware‘,
]`

Django項目的安裝並不強制要求任何中間件,如果你願意,MIDDLEWARE_CLASSES可以為空。中間件出現的順序非常重要:在request和view的處理階段,Django按照MIDDLEWARE_CLASSES中出現的順序來應用中間件,而在response和exception異常處理階段,Django則按逆序來調用它們。也就是說,Django將MIDDLEWARE_CLASSES視為view函數外層的順序包裝子:在request階段按順序從上到下穿過,而在response則反過來。以下這張圖可以更好地幫助你理解:

技術分享

  1. URLConf(URL映射)
如果處理request的中間件都沒有直接返回response,那麽Django會去解析用戶請求的URL。URLconf就是Django所支撐網站的目錄。它的本質是URL模式以及要為該URL模式調用的視圖函數之間的映射表。通過這種方式可以告訴Django,對於這個URL調用這段代碼,對於那個URL調用那段代碼。具體的,在Django項目的配置文件中有ROOT_URLCONF常量,這個常量加上根目錄"/",作為參數來創建django.core.urlresolvers.RegexURLResolver的實例,然後通過它的resolve方法解析用戶請求的URL,找到第一個匹配的view。

有關urlconf的內容,大家可以參考 [理解curlConf]()
  1. Template(模板)
大部分web框架都有自己的Template(模板)系統,Django也是。但是,Django模板不同於Mako模板和jinja2模板,在Django模板不能直接寫Python代碼,只能通過額外的定義filter和template tag實現。由於本文主要介紹Django流程,模板內容就不過多介紹。

最後歡迎大家觀看我關於 django + xadmin的教程:

http://coding.imooc.com/class/evaluation/78.html

django從請求到返回都經歷了什麽[轉]