誠實的說,直到目前為止,我並不欣賞django。在我的認知它並不是多麽精巧的設計。只是由功能堆積起來的"成熟方案"。但每壹樣東西的崛起都是時代的選擇。無論妳多麽不喜歡,但它被需要。希望有壹天,python能有更多更豐富的成熟方案,且不再被詬病性能和可維護性。(屁話結束)
取其精華去其糟粕,django的優點是方便,我們這次源碼閱讀的目的是探究其方便的本質。計劃上本次源碼閱讀不會精細到每壹處,而是大體以功能為單位進行解讀。
django-admin startproject HelloWorld 即可生成django項目,命令行是exe格式的。
manage.py 把參數交給命令行解析。
execute_from_command_line() 通過命令行參數,創建壹個管理類。然後運行他的 execute() 。
如果設置了reload,將會在啟動前先 check_errors 。
check_errors() 是個閉包,所以上文結尾是 (django.setup)() 。
直接看最後壹句 settings.INSTALLED_APPS 。從settings中抓取app
註意,這個settings還不是我們項目中的settings.py。而是壹個對象,位於 django\conf\__init__.py
這是個Settings類的懶加載封裝類,直到 __getattr__ 取值時才開始初始化。然後從Settings類的實例中取值。且會講該值賦值到自己的 __dict__ 上(下次會直接在自己身上找到,因為 __getattr__ 優先級較低)
為了方便debug,我們直接寫個run.py。不用命令行的方式。
項目下建個run.py,模擬runserver命令
debug抓壹下setting_module
回到 setup() 中的最後壹句 apps.populate(settings.INSTALLED_APPS)
開始看 apps.populate()
首先看這段
這些App最後都會封裝成為AppConfig。且會裝載到 self.app_configs 字典中
隨後,分別調用每個appConfig的 import_models() 和 ready() 方法。
App的裝載部分大體如此
為了方便debug我們改寫下最後壹句
res的類型是 Command <django.contrib.staticfiles.management.commands.runserver.Command object at 0x00000101ED5163A0>
重點是第二句,讓我們跳到 run_from_argv() 方法,這裏對參數進行了若幹處理。
用pycharm點這裏的handle會進入基類的方法,無法得到正確的走向。實際上子類Commond重寫了這個方法。
這裏分為兩種情況,如果是reload重載時,會直接執行 inner_run() ,而項目啟動需要先執行其他邏輯。
django 項目啟動時,實際上會啟動兩次,如果我們在項目入口(manage.py)中設置個print,會發現它會打印兩次。
第壹次啟動時, DJANGO_AUTORELOAD_ENV 為None,無法進入啟動邏輯。會進入 restart_with_reloader() 。
在這裏會將 DJANGO_AUTORELOAD_ENV 置為True,隨後重啟。
第二次時,可以進入啟動邏輯了。
這裏創建了壹個django主線程,將 inner_run() 傳入。
隨後本線程通過 reloader.run(django_main_thread) ,創建壹個輪詢守護進程。
我們接下來看django的主線程 inner_run() 。
當我們看到wsgi時,django負責的啟動邏輯,就此結束了。接下來的工作交由wsgi服務器了
這相當於我們之前在fastapi中說到的,將fastapi的app交由asgi服務器。(asgi也是django提出來的,兩者本質同源)
那麽這個wsgi是從哪來的?讓我們來稍微回溯下
這個settings是壹個對象,在之前的操作中已經從 settings.py 配置文件中獲得了自身的屬性。所以我們只需要去 settings.py 配置文件中尋找。
我們來尋找這個 get_wsgi_application() 。
它會再次調用 setup() ,重要的是,返回壹個 WSGIHandler 類的實例。
這就是wsgiapp本身。
load_middleware() 為構建中間件堆棧,這也是wsgiapp獲取setting信息的唯壹途徑。導入settings.py,生成中間件堆棧。
如果看過我之前那篇fastapi源碼的,應該對中間件堆棧不陌生。
app入口→中間件堆棧→路由→路由節點→endpoint
所以,wsgiapp就此構建完畢,服務器傳入請求至app入口,即可經過中間件到達路由進行分發。