bigywater.tech
Django · 88

collectstatic 的工作流程

collectstatic 从哪里收集?同名文件怎么选?再次运行会重新复制吗?这篇建两个 app 加一个项目 assets 目录,把这些行为逐个试出来,顺便发现「同一秒内改的文件会被漏掉」这个细节。

STATICFILES_DIRS 优先
同名文件:STATICFILES_DIRS 里的先于 app 的 static/,app 之间按 INSTALLED_APPS 顺序
秒级
增量比较修改时间并忽略亚秒精度:同一秒内改完再收集会被判为未修改
不自动删除
源里删掉的文件,目标(STATIC_ROOT)里的旧副本仍在,除非 --clear

收集与同名冲突

实测 · 本机执行输出
  6 static files copied to '<实验目录>', 2 skipped due to conflict.
  collected/ 下的文件: ['admin/override.txt', 'app1/a.css', 'app2/b.js', 'proj_only.js', 'shared.txt', 'site.css']
  shared.txt 的内容: FROM PROJECT assets   (STATICFILES_DIRS 里的先于 app 目录;app 之间按 INSTALLED_APPS 顺序)
  findstatic shared.txt -v2(所有候选位置):
  Found 'shared.txt' here:
  d88/assets/shared.txt
  d88/app1/static/shared.txt
  d88/app2/static/shared.txt
  Looking in the following locations:
  d88/assets

用 findstatic 文件名 -v2 可以查「某个静态文件到底来自哪里、还有哪些被遮蔽」。

增量收集

实测 · 本机执行输出
  无变化: 6 unmodified, 2 skipped due to conflict.
  刚改完 site.css 立刻运行(与上次复制在同一秒内): 6 unmodified, 2 skipped due to conflict. | 目标里的内容: body{color:red}
  等 1.2 秒后再改一次并运行: 5 unmodified, 2 skipped due to conflict. | 目标里的内容: body{color:green}
  内容变了但 mtime 没变: 6 unmodified, 2 skipped due to conflict. | console.log(1) (collectstatic 用修改时间判断,内容变而 mtime 不变会漏掉)

这个「同一秒内」的现象,我是在写脚本时无意中踩到的:改完文件立刻 collectstatic,发现没更新。真实部署里两次 collectstatic 通常隔得很久,但自动化测试脚本里会碰到。

清理与选项

实测 · 本机执行输出
  --dry-run: 0 static files copied to '<实验目录>', 6 unmodified, 2 skipped due to conflict.
  目标里多出来的旧文件 stale.txt(源里已不存在),不带 --clear 后: True (不会自动删除)
  --clear 之后 stale.txt 还在吗: False | 7 static files deleted, 6 static files copied to '<实验目录>', 2 skipped due to conflict.
  --link: 6 static files deleted, 6 static files symlinked to '<实验目录>', 2 skipped due to conflict. | site.css 是符号链接: True

部署脚本一般用 collectstatic --noinput --clear,避免旧文件累积(配合哈希文件名时,旧版本文件是否要保留给还在使用旧页面的客户端,是另一个取舍,第 89 篇)。

必需设置

实测 · 本机执行输出
  -i "*.js": [] (js 文件都被跳过) | 6 static files deleted, 4 static files copied to '<实验目录>', 2 skipped due to conflict.
  没有 STATIC_ROOT: django.core.exceptions.ImproperlyConfigured: You're using the staticfiles app without having set the STATIC_ROOT setting to a filesystem path.

开发时(runserver、DEBUG=True)静态文件直接从源目录提供,不需要 collectstatic。

分步演示:collectstatic

collectstatic
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 / Python 3.14.7 / Django 6.1.1(默认用 SQLite,其它数据库会在文中注明),用 django.test.Client 或真实的本机服务器进程实测;计时用 time.perf_counter,数字每次运行会有小幅抖动。SQLite 无关;静态文件由两个 app 与项目 assets 提供
    • 没有验证:远程存储(S3)后端的 collectstatic、--no-post-process、大量文件下的耗时、跨文件系统的 mtime 精度差异
    京ICP备2026067502号-1京公网安备11010502063487号