django 從請求到返回都經(jīng)歷了什么
ruserver是使用django自己的web server,主要用于開發(fā)和調(diào)試中, 部署到線上環(huán)境一般使用nginx+uwsgi模式
看一下manager.py的源碼,你會發(fā)現(xiàn)上面的命令其實是通過Django的execute_from_command_line方法執(zhí)行了內(nèi)部實現(xiàn)的runserver命令,那么現(xiàn)在看一下runserver具體做了什么。。
通過源碼分析可知, ruserserver主要完成兩件事:
1). 解析參數(shù),并通過django.core.servers.basehttp.get_internal_wsgi_application方法獲取wsgi handler;
2). 根據(jù)ip_address和port生成一個WSGIServer對象,接受用戶請求
get_internal_wsgi_application的源碼如下:

通過上面的代碼我們可以知道,Django會先根據(jù)settings中的WSGI_APPLICATION來獲取handler;
在創(chuàng)建project的時候,Django會默認創(chuàng)建一個wsgi.py文件,而settings中的WSGI_APPLICATION配置也會默認指向這個文件??匆幌逻@個wsgi.py文件,其實它也和上面的邏輯一樣,最終調(diào)用get_wsgi_application實現(xiàn)。
Django和其他Web框架一樣,HTTP的處理流程基本類似:接受request,返回response內(nèi)容。Django的具體處理流程大致如下:
在通過django-admin.py創(chuàng)建project的時候,Django會自動生成默認的settings文件和manager.py等文件,在創(chuàng)建WSGIServer之前會執(zhí)行下面的引用:
from django.conf import settings
上面引用在執(zhí)行時,會讀取os.environ中的DJANGO_SETTINGS_MODULE配置,加載項目配置文件,生成settings對象。所以,在manager.py文件中你可以看到,在獲取WSGIServer之前,會先將project的settings路徑加到os路徑中。
不管是使用runserver還是uWSGI運行Django項目,在啟動時都會調(diào)用django.core.servers.basehttp中的run()方法
創(chuàng)建一個django.core.servers.basehttp.WSGIServer類的實例,之后調(diào)用其serve_forever()方法啟動HTTP服務(wù)。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()
如上,我們可以看到:在創(chuàng)建WSGIServer實例的時候會指定HTTP請求的Handler,
上述代碼使用WSGIRequestHandler。當用戶的HTTP請求到達服務(wù)器時,
WSGIServer會創(chuàng)建WSGIRequestHandler實例,使用其handler方法來處理HTTP請求(其實最終是調(diào)用wsgiref.handlers.BaseHandler中的run方法處理)。
WSGIServer通過set_app方法設(shè)置一個可調(diào)用(callable)的對象作為application,上面提到的handler方法最終會調(diào)用設(shè)置的application處理request,并返回response。
其中,WSGIServer繼承自wsgiref.simple_server.WSGIServer,而WSGIRequestHandler繼承自wsgiref.simple_server.WSGIRequestHandler,wsgiref是Python標準庫給出的WSGI的參考實現(xiàn)。其源碼可自行到wsgiref參看,這里不再細說。
第二步中說到的application,在Django中一般是django.core.handlers.wsgi.WSGIHandler對象,WSGIHandler繼承自django.core.handlers.base.BaseHandler,這個是Django處理request的核心邏輯,它會創(chuàng)建一個WSGIRequest實例,而WSGIRequest是從http.HttpRequest繼承而來
上面提到的BaseHandler中有個get_response方法,該方法會先加載Django項目的ROOT_URLCONF,然后根據(jù)url規(guī)則找到對應(yīng)的view方法(類),view邏輯會根據(jù)request實例生成并返回具體的response。
在Django返回結(jié)果之后,第二步中提到wsgiref.handlers.BaseHandler.run方法會調(diào)用finish_response結(jié)束請求,并將內(nèi)容返回給用戶
首先給大家分享兩個網(wǎng)上看到的Django流程圖:

流程圖一

流程圖二
上面的兩張流程圖可以大致描述Django處理request的流程,按照流程圖2的標注,可以分為以下幾個步驟:
1. 用戶通過瀏覽器請求一個頁面
2.請求到達Request Middlewares,中間件對request做一些預(yù)處理或者直接response請求
3.URLConf通過urls.py文件和請求的URL找到相應(yīng)的View
4.View Middlewares被訪問,它同樣可以對request做一些處理或者直接返回response
5.調(diào)用View中的函數(shù)
6.View中的方法可以選擇性的通過Models訪問底層的數(shù)據(jù)
7.所有的Model-to-DB的交互都是通過manager完成的
8.如果需要,Views可以使用一個特殊的Context
9.Context被傳給Template用來生成頁面
? ? a.Template使用Filters和Tags去渲染輸出
? ? b.輸出被返回到View
? ? c.HTTPResponse被發(fā)送到Response Middlewares
? ? d.任何Response Middlewares都可以豐富response或者返回一個完全不同的response
? ? e.Response返回到瀏覽器,呈現(xiàn)給用戶
上述流程中最主要的幾個部分分別是:Middleware(中間件,包括request, view, exception, response),URLConf(url映射關(guān)系),Template(模板系統(tǒng)),下面一一介紹一下。
Middleware并不是Django所獨有的東西,在其他的Web框架中也有這種概念。在Django中,Middleware可以滲入處理流程的四個階段:request,view,response和exception,相應(yīng)的,在每個Middleware類中都有rocess_request,process_view, process_response 和 process_exception這四個方法。你可以定義其中任意一個活多個方法,這取決于你希望該Middleware作用于哪個處理階段。每個方法都可以直接返回response對象。
Middleware是在Django BaseHandler的load_middleware方法執(zhí)行時加載的,加載之后會建立四個列表作為處理器的實例變量:
_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可以為空。中間件出現(xiàn)的順序非常重要:在request和view的處理階段,Django按照MIDDLEWARE_CLASSES中出現(xiàn)的順序來應(yīng)用中間件,而在response和exception異常處理階段,Django則按逆序來調(diào)用它們。也就是說,Django將MIDDLEWARE_CLASSES視為view函數(shù)外層的順序包裝子:在request階段按順序從上到下穿過,而在response則反過來。以下這張圖可以更好地幫助你理解:

URLConf(URL映射)
如果處理request的中間件都沒有直接返回response,那么Django會去解析用戶請求的URL。URLconf就是Django所支撐網(wǎng)站的目錄。它的本質(zhì)是URL模式以及要為該URL模式調(diào)用的視圖函數(shù)之間的映射表。通過這種方式可以告訴Django,對于這個URL調(diào)用這段代碼,對于那個URL調(diào)用那段代碼。具體的,在Django項目的配置文件中有ROOT_URLCONF常量,這個常量加上根目錄"/",作為參數(shù)來創(chuàng)建django.core.urlresolvers.RegexURLResolver的實例,然后通過它的resolve方法解析用戶請求的URL,找到第一個匹配的view。
有關(guān)urlconf的內(nèi)容,大家可以參考 [理解curlConf]()
Template(模板)
大部分web框架都有自己的Template(模板)系統(tǒng),Django也是。但是,Django模板不同于Mako模板和jinja2模