用途:控制 Django 启动时间,把重型导入从 django.setup() 路径上移走,因为 web、celery、temporal、migrate、shell、CI 每个进程都要为这条路径付出代价。适用于改动 AppConfig.ready()、信号接收器接线、模型注册或路由时。
主要能力与机制:涉及四个机制(惰性路由、模型注册、接收器接线、启动 GC 窗口)与回归防护。防护测试位于 posthog/test/test_startup_import_budget.py;失败时应延迟导入,而不是删条目绕过。若刻意把某个重型库移出 setup,需在确认裸 django.setup() 中不存在后加入 FORBIDDEN_AT_SETUP。test_no_new_heavy_imports_at_setup 会捕获未命名的新导入:任何不在 posthog/test/setup_import_baseline.txt 中、在 setup 时耗时 ≥100ms 的包都会导致构建失败,应延迟导入;基线只保留每个进程在 setup 时确实需要的包。
默认做法:新应用/产品从 models/init.py 注册模型,从 AppConfig.ready() 接线接收器并保持 ready() 轻量;新接收器在所属 AppConfig.ready() 中从专用轻量模块(activity_logging.py、signals.py)接线,绝不通过 API/viewset 模块;新重型依赖(厂商 SDK、Temporal/AI/ClickHouse、pandas/pyarrow/scipy)在使用路径上函数内导入并加 # noqa: PLC0415,不放模块级;setup 路径模块上的 schema 类型,枚举用 posthog.schema_enums,pydantic 模型只在用到的方法内从 posthog.schema 导入;setup 路径上不要模块级 from posthog.tasks...,CeleryQueue 位于 posthog.celery_queues;新 viewset/路由不再在 setup 加载,路由放 rest_router.py。任何延迟都会转移成本,需判断现在由哪个进程、在哪条路径上承担,以及该路径是否对延迟敏感。
提交前需检查的陷阱:ready() 重新把重型依赖拖回启动;接收器静默丢失;模型从注册表消失;惰性路由合并冲突;聚合包 init 的 PEP 562 垫片;序列化器字段 kwargs 在导入时执行;再导出消失;补丁目标在调用时导入后失效;错误合并后重新生成快照;测量的是包装而非实际工作;GC 造成的虚假 importtime 成本。完整细节见 docs/internal/django-startup-time.md。