## 背景 `sys_user.dept_id → sys_dept.id` 与 `sys_dept` 的审计字段(`created_id`/`updated_id`/`deleted_id` → `sys_user.id`,来自 `UserMixin`)构成循环外键。 当前 dev 分支通过给 `dept_id` 加 `use_alter=True` 规避该循环(`backend/app/modules/system/user/model.py:84`)。 ## 问题 `use_alter` 在 `create_all` 建表场景下没问题,但一旦项目引入 Alembic 迁移(autogenerate),会**静默丢失外键**: 1. autogenerate 生成基线迁移时,会把 `use_alter=True` 的约束**内联**进 `op.create_table`,而不是拆成独立的 `op.create_foreign_key`; 2. 执行 `op.create_table` 时,PostgreSQL 方言编译 `CREATE TABLE` 会跳过 `use_alter` 约束(它预期该约束由后续 ALTER 单独发出); 3. 但 `op.create_table` 不会像 `MetaData.create_all` 那样补发 ALTER,于是 `sys_user.dept_id` 外键被静默丢失。 已在 PostgreSQL 实测复现:`op.create_table` 后 `inspect.get_foreign_keys("sys_user")` 返回空,且 `alembic check` 会报模型与库不一致。 ## 建议 与其用 `use_alter` 标记循环边,不如消除循环根源:去掉 `UserMixin` 三个审计字段(`created_id`/`updated_id`/`deleted_id`)指向 `sys_user` 的数据库级外键约束(`ondelete=SET NULL` 已允许悬空,审计字段本就不需要强引用语义),`created_by`/`updated_by`/`deleted_by` 关系改用显式 `primaryjoin` 保持可查。 这样循环依赖消失,autogenerate 无需 `use_alter` 即可正确生成迁移。已在 fork 上验证(`pytest` 74 passed)。 若该方向可行,我可基于 dev 分支提交对应 PR(改动 `base_model.py` 与 `modules/system/user/model.py`)。
Activity
fastapiadmin commentedon Oct 22, 2026
已修复并合并到
dev:PR #541(合并提交45a8ade9)。验证要点:
sys_dept只剩parent_id自引用外键、sys_user只剩dept_id→ 循环外键消除created_by/updated_by/deleted_by改用显式primaryjoin,local/remote pairs =表.created_id → sys_user.id,selectinload可正常执行后续项(不阻塞):仓库 Alembic 目前只有
env.py无版本,线上既有库仍带旧外键;将来引入基线迁移时需补一条drop_constraint。