bigywater.tech
Django · 82

Django Template Loader 加载顺序

同名模板出现在多个地方时谁赢?怎么覆盖 admin 的模板?找不到模板时怎么排查?cached 加载器到底省多少?这篇建两个 app 加一个项目模板目录,把这几件事都试一遍。

DIRS 优先
项目 DIRS 里的模板优先于 app 里的同名模板,app 之间按 INSTALLED_APPS 顺序
tried
TemplateDoesNotExist 带着尝试过的每个路径,调试时直接看
1.17 → 0.0010 ms
没有 cached vs 有 cached 加载器,get_template 每次的耗时(本机)

谁赢

实测 · 本机执行输出
shared.html → shared FROM project templates dir
loaders: ['Loader']  内部: ['Loader', 'Loader']
only1.html → only in app1 | only2.html → only in app2
列出 shared.html 的所有候选(按尝试顺序):
    Loader ['<实验目录>']
    Loader ['<实验目录>', '<实验目录>']

加载器按 TEMPLATES[0]['OPTIONS']['loaders'] 的顺序尝试(默认:先 filesystem 再 app_directories,外面包一层 cached)。

INSTALLED_APPS 的顺序

实测 · 本机执行输出
DIRS 为空,INSTALLED_APPS = [app2, app1] → shared FROM app2
DIRS 为空,INSTALLED_APPS = [app1, app2] → shared FROM app1

同名模板的冲突是 Django 里最常见的「莫名其妙」之一,报错里的 tried 列表告诉你每个位置的状态。

missing 与 select_template

实测 · 本机执行输出
异常: missing.html
chain 里的引擎异常数: 1
尝试过的位置共 3 条:
    d82/templates/missing.html → Source does not exist
    d82/app1/templates/missing.html → Source does not exist
    d82/app2/templates/missing.html → Source does not exist
select_template 选到了第一个存在的: only1.html

注意这里能看到的是引擎的 chain,而不是直接挂在最外层异常上的 tried(外层的是空的)。

cached 加载器

实测 · 本机执行输出
  cached loader  第一次 get_template    2.909 ms   之后每次(300 次中位)   0.0010 ms
  没有 cached      第一次 get_template    2.814 ms   之后每次(300 次中位)   1.1701 ms
  DEBUG=True(未显式设置 loaders)时的加载器: ['django.template.loaders.cached.Loader']
  DEBUG=False(未显式设置 loaders)时的加载器: ['django.template.loaders.cached.Loader']

cached 加载器把编译后的模板放在进程内存里。开发时改模板会自动重置缓存(第 25 篇)。没有 cached 的对照组每次 get_template 要读文件并编译。

分步演示:模板加载

模板加载器
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。三个候选位置由 get_template_sources 列出;耗时为 300 次中位数,模板是循环 40 组的合成模板
    • 没有验证:多个 TEMPLATES 引擎(Jinja2)并存、加载器的 Origin 与 skip_template 机制、模板热重载在大项目里的开销、cached 加载器缓存的失效边界
    京ICP备2026067502号-1京公网安备11010502063487号