bigywater.tech
Django · 89

ManifestStaticFilesStorage 文件名映射

静态文件名里那串哈希是怎么来的?CSS 里引用的图片怎么跟着变?模板引用了没收集的文件会怎样?这篇跑一遍 collectstatic + ManifestStaticFilesStorage,把每个环节的产物都展示出来。

12 位
文件名里的哈希是内容 MD5 的前 12 位;内容不变名字不变
url() 被改写
CSS 里的 url() / @import 被改写成带哈希的名字;外部 URL 与 data: 不动
ValueError
DEBUG=False 时 {% static %} 引用清单里没有的文件,直接抛 Missing staticfiles manifest entry

文件名与映射表

实测 · 本机执行输出
  5 static files copied to '<实验目录>', 5 post-processed.
  收集到的文件(原名 + 带哈希的副本):
    css/base.7411b9041dad.css
    css/base.css
    css/site.8ba4b5966a42.css
    css/site.css
    img/bg.b04ebc27a009.png
    img/bg.png
    img/logo.79b11419e142.png
    img/logo.png
    js/app.5c4e6521f9b0.js
    js/app.js
    staticfiles.json
  staticfiles.json 的键: ['paths', 'version', 'hash']   paths 映射:
    css/base.css → css/base.7411b9041dad.css
    css/site.css → css/site.8ba4b5966a42.css
    img/bg.png → img/bg.b04ebc27a009.png
    img/logo.png → img/logo.79b11419e142.png
    js/app.js → js/app.5c4e6521f9b0.js

模板里 {% static "css/site.css" %} 在生产环境(DEBUG=False)会查这张表,输出带哈希的 URL。

CSS 里的引用

实测 · 本机执行输出
  css/site.8ba4b5966a42.css 的内容:
    @import url("base.7411b9041dad.css"); .logo{background:url("../img/logo.79b11419e142.png")} .bg{background:url("../img/bg.b04ebc27a009.png?v=1#x")} .ext{background:url('https://cdn.example.com/x.png')} .data{background:url(data:image/png;base64,AAAA)}

这一步叫 post_process。因为要先算被引用文件的哈希,所以需要多趟处理(max_post_process_passes,本文没有测)。

哈希与缓存失效

实测 · 本机执行输出
  app.js 内容的 md5[:12] = 5c4e6521f9b0  与 manifest 里的文件名一致: True
  再运行一次(内容没变),映射不变: True
  改了 app.js: js/app.5c4e6521f9b0.js → js/app.a2f0c2d96218.js
  旧的哈希文件还留在目录里: True (不会自动删除,需要 --clear 或者定期清理)

有了哈希文件名,就可以给静态文件设很长的缓存时间(第 90 篇)。代价是旧版本文件累积;部署时要不要清理,取决于是否需要兼容还没刷新的旧页面。

清单缺项

实测 · 本机执行输出
  ValueError: Missing staticfiles manifest entry for 'img/missing.png'
  <a href="/static/css/site.8ba4b5966a42.css">

常见触发场景:本地开发一直 DEBUG=True 没发现,上线后某个 {% static %} 引用了不存在的文件。CI 里可以对所有模板做一次 DEBUG=False 的渲染检查(本文没有做)。

manifest_strict

实测 · 本机执行输出
  非严格: img/late.png → /static/img/late.7f02e16a4a58.png
  非严格: img/missing.png → ValueError: The file 'img/missing.png' could not be found with <proj.storage.NonStrict objec
  严格: img/late.png → ValueError: Missing staticfiles manifest entry for 'img/late.png'

非严格模式是「宽容」而不是「万能」:文件真的不存在仍然报错。

分步演示:文件名哈希

ManifestStaticFilesStorage
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。STORAGES["staticfiles"] 设置为 ManifestStaticFilesStorage;DEBUG=False
    • 没有验证:ManifestFilesMixin 的 manifest_storage、大项目的 post_process 耗时、source map 的引用改写、JS 里 import 的引用(需要 Django 4.2+ 的 support_js_module_import_aggregation 选项)
    京ICP备2026067502号-1京公网安备11010502063487号