> 检查渲染产物,不是源码——Django 的 {# #} 只支持单行
Django 模板的 {# ... #} 注释只支持单行。跨行写的那一刻它就不是注释了, 会被原样渲染给每一个访问者:
// TABLE_OF_CONTENTS
结论先行¶
Django 模板的 {# ... #} 注释只支持单行。跨行写的那一刻它就不是注释了,
会被原样渲染给每一个访问者:
{# 这一行是注释
这一行会显示在页面上 #} ← 访客能看见这两行全部内容
跨行注释必须用 {% comment %} ... {% endcomment %}。
更一般的教训——这才是要记住的部分:
验证要在渲染后的产物上做,不能在源码上做。
在源码里 grep {# 只能告诉你"语法写了",告诉不了你"它有没有漏出去"。
为什么¶
这个 bug 在一个公开站点的中文简历页上活了几周,是用户发现的,不是我发现的。 只在中文模板里出现(英文模板没有那段注释),所以只看一种语言的页面查不出来。
它能活下来的原因很典型:
- 源码看起来是对的。
{#和#}都在,视觉上就是一条注释。 - 本地开发时可能看不出来。
DEBUG=False会自动启用缓存模板加载器, 改完模板必须docker restart才生效——生产和本地的行为不一致。 - 没有任何报错。 模板引擎认为这是合法文本,因为它确实是。
同一个 bug 在另一个组件里潜伏着(一个 6 行的 {# #} 文件头注释),
只是因为还没有任何页面 include 那个文件才没发作。
"没出问题"和"没有 bug"是两回事。
怎么用¶
这一条的检查(改任何模板之后跑):
# 每个页面都应该是 0
for p in / /about/ /notes/ /resume/ '/resume/?lang=en'; do
n=$(curl -s "https://example.com$p" | grep -cE '\{#|\{%|endcomment')
echo "$n $p"
done
两个容易漏的点:
- 多语言页面要各查一遍。 如果站点默认渲染中文、英文在 ?lang=en,
只请求 /resume/ 会漏掉英文侧的问题,反之亦然。
- 只 include 而不直接访问的组件查不到。这类文件要单独审。
一般化:任何"源码 → 产物"的管线,都要在产物侧验。
| 管线 | 别只查 | 要查 |
|---|---|---|
| 模板 → HTML | .html 模板源码 |
curl 回来的响应体 |
| LaTeX → PDF | .tex 源码 |
pdftotext 之后的文本(空章节、浮动体孤儿) |
| requirements → 容器 | requirements*.txt |
pip freeze 出来的已安装集合 |
| 配置 → 运行时 | .env / settings.py |
进程实际读到的值 |
这四条是同一个错误的四个化身:检查了意图,没检查结果。 LaTeX 那一行有独立的血案——编译出的 PDF 里一整节是空的, 自审五轮全部只看源码,没有一轮看过编译产物(见 自审的失效模式不是"没发现",是"发现了没执行")。
推论: 部署脚本的最后一步应该是"拉回产物并检查",而不是"部署命令返回 0"。
docker cp 成功不代表 Gunicorn 重载了;git push 成功不代表线上生效了。
出处¶
MyCMS/resume/templates/resume/resume_cn.html(发作现场)、MyCMS/templates/components/pagination.html(同样的 bug,潜伏中)- 相关的已发布笔记:
audit-the-deployed-set(requirements 那一行的完整版)、fail-open-defaults-env-var-drift(配置那一行)