实测 HttpRequest 的请求体:body / POST / read() 三种读取的先后顺序在 urlencoded、multipart、JSON 下各是什么结果(read() 之后 POST 会静默为空);PUT / PATCH 不解析 POST;DATA_UPLOAD_MAX_MEMORY_SIZE 默认 2.5 MiB 超过一字节即被拒。
实测 QueryDict:重复参数下 q[key]、get、getlist、dict()、q.dict() 各返回什么;默认不可变、copy() 后才能改;GET / POST 里同样只有 getlist 能取全部;字段数上限 1000,超过一个即 TooManyFieldsSent。
实测 HttpResponse / JsonResponse:默认头与 charset;响应头大小写不敏感且后写覆盖;JsonResponse 对 datetime / Decimal / UUID 的编码与 safe=False 的含义;响应头里的换行会被 BadHeaderError 拒绝;Content-Length 由 CommonMiddleware 补上。
在 gunicorn 上实测 StreamingHttpResponse:同样 5 段、每段 0.3 s,HttpResponse 首字节 1507 ms,流式 304 ms;200 MiB 响应的 worker 峰值内存 241 MiB vs 43 MiB;流式响应没有 .content 也没有 Content-Length。
实测条件 GET:ConditionalGetMiddleware 自动 ETag 只省带宽、视图照常执行;@condition 用 etag / last_modified 函数提前判断,命中时不执行视图(调用次数不增);If-None-Match 与 If-Modified-Since 的各种取值的结果,POST 带命中的 If-None-Match 得到 412。
对比 wsgi.py 与 asgi.py 的启动:导入即 django.setup() 并构建中间件链;调用约定 (environ, start_response) vs (scope, receive, send);同步视图与 async 视图在两种入口下分别运行在哪个线程;Django 自带的 ASGIHandler 收到 lifespan 事件会抛 ValueError。
实测 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。