实测自定义 QuerySet + as_manager() 的链式调用、覆盖默认 Manager 做软删除;关系访问(book.author)与 JOIN 不走自定义 Manager,已软删除的对象仍能被取到;默认 Manager 是模型里第一个定义的。
写一个 list ↔ 逗号文本的自定义字段,记录 to_python / from_db_value / get_prep_value / value_to_string 在构造、保存、查询、values_list、full_clean、序列化时各自被调用的时刻;并验证 deconstruct 漏写参数会让 makemigrations 检测不到变化。
实测自定义 Lookup(__ne)与 Transform(__abs):生成的 SQL、叠加使用、在 values_list / order_by 里的使用;注册在 Field 基类上会让所有字段都有它;PostgreSQL EXPLAIN 显示 Lower(name) 函数索引对 annotate(Lower) 有效,对 __iexact(UPPER)无效。
拆开 Django ORM 生成 SQL 的过程:QuerySet 包着的 Query 是条件树而不是字符串;compiler 按数据库后端编译出不同方言(SQLite LIKE vs PostgreSQL UPPER(...) LIKE UPPER(...));构建 65 µs、编译 84 µs、整体 262 µs;看 SQL 的几种办法。
用 QuerySet.explain() 在 PostgreSQL 与 SQLite 上看执行计划:无索引 Seq Scan、加索引后 Bitmap Index Scan(估算成本 3774 → 536)、选择性差时仍全表扫描;analyze=True 给出实际耗时(0.175 ms),buffers / format 选项;explain 只能用在 QuerySet 上。
在 gunicorn + PostgreSQL 上观察 Django 的数据库连接生命周期:连接是惰性的;默认每个请求一个新的数据库后端进程(6 个请求 6 个 pid);请求结束连接被关闭;连接是线程局部的(4 个线程 = 4 个连接);CONN_MAX_AGE=None 时连接在请求结束后仍保持。
量 CONN_MAX_AGE:300 个请求中位延迟 2.78 ms(默认)→ 0.65 ms(60 秒);默认 300 个请求 300 个连接,持久连接只用 1 个;但持久连接数 = worker × 线程,max_connections=30 的库上开第二个服务就出现 too many clients。
在 PostgreSQL 上记录 transaction.atomic() 的实际 SQL:外层 BEGIN/COMMIT、内层 SAVEPOINT/RELEASE;内层异常只回滚到保存点;atomic 里吞掉 IntegrityError 而不包内层 atomic 会导致后续查询全部 TransactionManagementError;durable 与 savepoint=False 的行为。
实测 transaction.on_commit():外层事务提交后才执行(此时别的连接已能看见数据);回滚与保存点回滚都会丢弃回调;autocommit 下立即执行;一个回调抛异常会阻止后面的回调,robust=True 则继续;回调里的写入处于 autocommit。
在 PostgreSQL 上做行锁实验:select_for_update 必须在 atomic 里;持锁期间普通调用阻塞约 1 秒、nowait=True 立即 OperationalError、skip_locked=True 返回 0 行;skip_locked 让 3 个线程并发消费 30 个任务恰好各一次;of 限定锁的表。
在 PostgreSQL 上实测三种隔离级别下的 Django 行为:默认 READ COMMITTED 会不可重复读(100 → 200);REPEATABLE READ 读到同一快照,但并发更新同一行会 OperationalError: could not serialize access due to concurrent update,需要应用重试。
实测 ATOMIC_REQUESTS:视图抛异常时整个视图的写入回滚,但返回 500 响应(不是异常)时照样提交;中间件的写入在事务之外;@non_atomic_requests 例外;on_commit 的时机随之改变;流式响应的生成器写入在事务之外。