bigywater.tech
Django · 11

Django 项目启动过程:manage.py 到 django.setup()

manage.py 敲下回车之后,Django 到底按什么顺序把 settings、apps、models、urls 一个个拉起来?这篇在项目的每个入口都加了打点,不靠记忆里的源码顺序,直接看时间戳。

+60 ms
manage.py check 里 ready() 被调用的时刻(从第一次读 settings 起算)
按需
urls / views / 中间件不在 setup() 里导入,首个请求(或系统检查)才导入
103→129 ms
django.setup() 中位耗时:1 个 app → 加上 admin、staticfiles(本机)

manage.py check 的时间线

实测 · 本机执行输出
  命令输出: System check identified no issues (0 silenced).  整个进程 204 ms
  +    0.0 ms  import proj/settings.py(首次访问 settings 才会导入)
  +   42.2 ms  import lab/apps.py(AppConfig 所在模块被导入)
  +   42.2 ms  LabConfig.__init__(创建 AppConfig 实例)
  +   60.0 ms  import lab/models.py
  +   60.3 ms  LabConfig.ready()
  +   63.4 ms  import proj/urls.py(ROOT_URLCONF)
  +   63.7 ms  import lab/views.py
  +   68.0 ms  import proj/mw.py

每个模块顶部都写了一行打点,记录被导入的时刻;AppConfig.__init__ 与 ready() 里同样打点。settings 是第一个出现的——它在第一次读取设置时才导入。接着 django.setup() 里按次序发生:导入 apps.py、创建 AppConfig 实例、导入 models、调用 ready()。urls、views、中间件模块在这条命令里晚一点才被导入(系统检查用到了它们)。

处理一次请求时的顺序

实测 · 本机执行输出
  +    0.0 ms  import proj/settings.py(首次访问 settings 才会导入)
  +   10.3 ms  import lab/apps.py(AppConfig 所在模块被导入)
  +   10.4 ms  LabConfig.__init__(创建 AppConfig 实例)
  +   25.7 ms  import lab/models.py
  +   26.0 ms  LabConfig.ready()
  +   42.7 ms  --- django.setup() 已完成 ---
  +   42.7 ms  --- 发出第 1 个请求 ---
  +   42.9 ms  import proj/mw.py
  +   43.0 ms  MW.__init__(中间件链在首次请求前构建)
  +   43.1 ms  MW.__call__ 进入
  +   43.2 ms  import proj/urls.py(ROOT_URLCONF)
  +   43.3 ms  import lab/views.py
  +   43.4 ms  view: hello()
  +   43.5 ms  MW.__call__ 返回
  +   43.6 ms  --- 第 1 个请求结束 status=200 ---
  +   43.6 ms  --- 发出第 2 个请求 ---
  +   43.8 ms  MW.__call__ 进入
  +   43.8 ms  view: hello()
  +   43.8 ms  MW.__call__ 返回
  +   43.9 ms  --- 第 2 个请求结束 ---

用 django.test.Client 在同一个进程里发两个请求。setup() 结束之后什么也没导入;第一个请求才导入中间件、ROOT_URLCONF、视图模块,并构建中间件链(MW.__init__);第二个请求只走 __call__ 和视图。所以「第一个请求比后面慢」的一部分就是这些模块的导入(用测试客户端量的,不等于真实服务器的首请求耗时)。

setup() 有多贵

实测 · 本机执行输出
  只有 contenttypes                              django.setup() 中位 102.8 ms
  contenttypes+auth+sessions+messages          django.setup() 中位 121.2 ms
  上面 + admin + staticfiles                     django.setup() 中位 128.8 ms

三种 INSTALLED_APPS 规模各在全新进程里 django.setup() 五次取中位数:数字随 app 增加缓慢增长。这个数字包含了导入 Django 自身模块的时间(manage.py 每个命令都要付一次),所以命令行工具启动慢主要是它。

分步演示:启动的三个阶段

Django 启动过程
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。事件时间取自项目里每个模块顶部写入日志文件的 perf_counter 时间戳(相对第一条事件)
    • 没有验证:django.setup() 内部各函数(configure_logging、set_script_prefix)的耗时占比、runserver 与 gunicorn 下的首请求耗时、Python 字节码缓存(.pyc)冷热对启动时间的影响、不同 Python 版本之间的差别
    京ICP备2026067502号-1京公网安备11010502063487号