实测 runserver 自动重载:默认 StatReloader;修改被导入的 .py 后 0.2–0.8 s 内新代码生效且进程被重启;修改模板不重启、缓存约 0.5 s 内刷新;没被导入的 .py 和非 .py 文件不触发重启。
用自定义检查实测 Django 系统检查框架:Error 会阻止 check / migrate / runserver / test 等命令,Warning 默认只提示(--fail-level 可升级为失败);--tag 选择检查、SILENCED_SYSTEM_CHECKS 静音、check --deploy 在开发设置下报出 6 个警告。
用查询捕获数出 QuerySet 每种操作发了几次查询:链式调用 0 次、第一次迭代 1 次并缓存;count / exists / [0] / bool / len / repr 各自的 SQL(repr 是 LIMIT 21);exists() 与 iterator() 不填充缓存;缓存会让同一个 QuerySet 看不到之后的新数据。
在 2000 本书 / 100 个作者 / 20 个标签上实测:外键不优化 2001 次查询 214 ms,select_related 1 次 10 ms;多对多用 prefetch_related 2 次;select_related 不能用于多对多;预取后再 filter 又会 N 次查询。
实测 only() / defer():生成的 SQL 与被推迟字段;读了被推迟的字段会每行多一次查询(200 行 1 → 201 次);2000 行 × 10 KB 的 body 不同取法耗时 12.5 / 5.1 / 1.2 ms;带推迟字段的实例 save() 只更新已加载的列。
实测 aggregate / annotate 与 SQL:没有关联行时 Count = 0、Sum = None;同时对两个多值关系聚合会被 JOIN 放大(ann 的书数 7 而不是 3);filter 放在 annotate 前后含义不同(WHERE / 再 JOIN / HAVING);Count(filter=Q) 生成 FILTER (WHERE);分组查询不套 Meta.ordering。
在 PostgreSQL 上用 8 个线程各加 200 次计数:读-改-写最终 335(丢失 1265 次,5 轮都丢约 1260 次),update(n=F("n") + 1) 精确得到 1600;并对比两种写法的 SQL。
实测 Q 对象:& | ~ 对应的 SQL、动态拼条件与空 Q 的陷阱(空 Q 在 | 里被忽略);多值关系上 filter(a, b) 与 filter(a).filter(b) 的语义差别;exclude 用 NOT EXISTS 子查询,与 filter 取反不同。
5 万行读入内存:模型对象 134 ms / 42 MiB,values() 29 ms / 17 MiB,values_list(flat=True) 18 ms / 5.1 MiB;only() 省内存但仍构造对象;values 系列拿不到模型上的方法。
20 万行上量 exists() / count() / len() / bool():exists 0.22 ms,count 2.48 ms,len 与 bool 各 615 ms 且占约 137 MiB;len(list(values_list)) 也要 59 ms;以及缓存对答案的影响。
在 PostgreSQL 上用 8 个线程并发 get_or_create:有唯一约束时每轮恰好 1 行、无异常;没有唯一约束时 20 轮全部出现重复行,之后 get_or_create 永久抛 MultipleObjectsReturned;update_or_create 同样依赖唯一约束。
实测 bulk_create / bulk_update:5000 条循环 save() 5000 次查询 1777 ms,bulk_create 1 次 66 ms;bulk 不触发信号;ignore_conflicts 不回填 pk;多表继承不支持;本机 SQLite 上 bulk_update 未必比逐个 save 快。