bigywater.tech
Django · 87

模板继承与 Block 渲染

{% extends %} 和 {% block %} 是怎么把几层模板拼成一个页面的?{{ block.super }} 取的是哪一层?加载模板要读几次盘?这篇用三层模板和一个带日志的加载器把这些都摊开。

block.super
取「上一层同名 block」的内容,不是最顶层
读盘
继承 N 层 = 无 cached 时读 N 次文件;cached 加载器缓存
不存在的 block
子模板定义了父链里没有的 block,被静默忽略

三层继承

实测 · 本机执行输出
base    → [base-top]base-title|base-content|base-footer[base-bottom]
mid     → [base-top]mid-title/base-title|mid-content(mid-inner)|base-footer[base-bottom]
leaf    → [base-top]mid-title/base-title|leaf-content/mid-content(leaf-inner)|base-footer[base-bottom]
leaf2   → [base-top]base-title|c2|base-footer[base-bottom]

渲染从最子的模板开始:它 extends 的链一路向上,直到根模板;最后根模板被渲染,遇到 block 时取链上「最子的」那个定义。

被忽略与报错

实测 · 本机执行输出
leaf.html 里定义了 nosuch block(父模板没有):输出里出现 IGNORED 吗 → False
{% extends %} 前面有文字: text before [base-top]base-title|base-content|base-footer[base-bottom]
同一个模板里定义两个同名 block: TemplateSyntaxError: Template: <实验目录>

拼错 block 名是常见 bug:子模板的内容因此被静默忽略,页面上看不到——排查时先核对 block 名。

读盘次数

实测 · 本机执行输出
extends 变量: [base-top]base-title|dyn|base-footer[base-bottom]
渲染 base.html 加载: ['base.html']
渲染 mid.html 加载: ['mid.html', 'base.html']
渲染 leaf.html 加载: ['leaf.html', 'mid.html', 'base.html'] (三层继承 = 三次读盘;cached 加载器会缓存)

这是 filesystem 加载器的读盘计数(手工在加载器的 get_contents 上加了日志)。

include 与 extends

实测 · 本机执行输出
with_inc.html → [base-top]base-title|inc(1|inc-block)|base-footer[base-bottom]
加载: ['with_inc.html', 'base.html', 'inc.html']
inc.html 里的 block b 被外层 extends 覆盖了吗:输出仍是 inc-block → True

选择:「页面骨架 + 可替换区域」用 extends;「可复用的片段」用 include(带参数用 with,隔离用 only,后者本文没有测)。

耗时

实测 · 本机执行输出
base.html         5.3 µs
leaf2.html       10.8 µs
leaf.html        21.5 µs

合成模板,渲染很小;真实页面的开销主要在数据库和你的变量。

分步演示:模板继承

模板继承
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。用 render_to_string;读盘次数由 monkey-patch filesystem.Loader.get_contents 记录
    • 没有验证:extends 必须是第一个标签这一文档要求的报错情形(本次实测有文字在前也没报错,原因没有查)、block 里的 {% if %} 与 super 的组合、自定义 tag 里的 block 渲染、include 的 only 参数
    京ICP备2026067502号-1京公网安备11010502063487号