在 PostgreSQL 上让迁移的第二条操作失败:默认 atomic 时整个迁移回滚,库里无残留;atomic = False 时第一条已提交而迁移没有记录(半应用),再次 migrate 会因列已存在而失败;恢复的办法是让实际状态与 django_migrations 记录重新一致,或用 --fake。
实测 --fake 与 --fake-initial:库里已有同名表时直接 migrate 报 relation already exists;--fake-initial 检查表存在就只记录;但它不核对列,少一列也 FAKED,之后写入才失败;--fake 只写记录不碰表,回退同样只删记录。
两个分支各生成一个依赖 0001 的迁移时 migrate / makemigrations 都拒绝工作;makemigrations --merge 生成空操作的合并迁移;合并不检查两个分支是否兼容——同一字段加不同类型,SQLite 悄悄改成后者,PostgreSQL 直接报错。
实测 squashmigrations:5 个迁移优化成 1 个操作(SQL 17 → 1 条);新旧迁移共存时,全新数据库走合并迁移,部分应用的数据库继续走旧迁移;replaces 声明它替代的迁移。
在 200 万行的 PostgreSQL 表上,一边每 5 ms 写一行一边跑迁移:加列几乎不阻塞(1 ms);integer → bigint 重写整表阻塞写入 1220 ms;改 NOT NULL 286 ms;普通建索引 587 ms,CONCURRENTLY 只有 1 ms。
200 万行 PostgreSQL 表上给字段加默认值:常量默认值 0.25 s 且 Django 会 DROP DEFAULT;callable default(uuid4)整表拿到同一个值(1 个不同值);db_default 保留数据库默认值;volatile 函数默认值 1.52 s(重写整表);null → 分批回填 → NOT NULL 的三步法。
实测 dumpdata / loaddata:输出格式与 natural key;loaddata 不调用自定义 save()、auto_now 保留文件里的值、post_save 以 raw=True 触发;同 pk 是覆盖;坏字段与坏外键的报错;1 万行 dumpdata 0.19 s、loaddata 1.41 s。
用带日志的字段与表单实测 Form 完整校验流程:构造时不校验;is_valid() 触发;每个字段 to_python → validate → run_validators → clean_<field>;再表单级 clean() 与 _post_clean();字段失败时 clean_<field> 被跳过、cleaned_data 缺该键;disabled 与多余字段的处理。
实测 Django 校验器:模型字段的 validators 在 save() 时不运行、full_clean() 才运行;所有 validators 错误累加;ModelForm 在 _post_clean 里运行模型校验;unique 由 full_clean 查库、save() 由数据库约束兜底;可配置校验器需要 @deconstructible。
实测 Form 与 ModelForm 的区别:字段自动生成、fields = "__all__" 让用户改到 is_featured(白名单则被忽略)、校验包含 unique 与模型 validators、校验失败后 instance 在内存里已被改写。
实测 ModelForm.save(commit=False):不发任何查询、返回 pk=None 的对象;多对多要等 save_m2m();忘了 save_m2m() 时多对多悄悄丢失且不报错;标准写法是补上服务端字段后 save() 再 save_m2m()。
实测 ModelForm 多对多保存:save_m2m 用 set() 做差集(标签 {a,b} → {b,c} 只 DELETE a、INSERT c);取消全部勾选会清空;queryset 是校验白名单;自定义 through 模型的额外字段只能取默认值。