Skip to content

Commit f408aeb

Browse files
committed
docs: design foreign key restoration
1 parent eff4ec8 commit f408aeb

1 file changed

Lines changed: 76 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# Restore Database Foreign Keys Design
2+
3+
## Goal
4+
5+
Restore every foreign-key relationship removed or omitted by the normalized user-schema change so SQLAlchemy can configure all mappers, PostgreSQL enforces referential integrity, migrations remain safe for databases that already applied `e73c215d7221`, and `make seed` completes successfully.
6+
7+
## Root Cause
8+
9+
The current ORM models declare relationship properties but no `ForeignKey` metadata. SQLAlchemy therefore cannot infer joins such as `permissions.id` to `role_permissions.permission_id`, causing mapper configuration to fail before the seed query executes.
10+
11+
The latest normalization migration was generated from that incomplete metadata. It drops six established constraints:
12+
13+
- `permissions.resource_id` to `authorization_resources.id`
14+
- `role_permissions.role_id` to `roles.id`
15+
- `role_permissions.permission_id` to `permissions.id`
16+
- `todos.user_id` to `users.id`
17+
- `user_has_roles.user_id` to `users.id`
18+
- `user_has_roles.role_id` to `roles.id`
19+
20+
It also creates seven normalized user tables whose `user_id` columns are `VARCHAR(36)` even though `users.id` is UUID, and omits their intended constraints.
21+
22+
## Design
23+
24+
### ORM metadata
25+
26+
Each affected model column will declare a SQLAlchemy `ForeignKey` targeting the parent table. Existing relationship names, `back_populates` pairs, collection shapes, and ORM cascade settings remain unchanged.
27+
28+
The seven normalized user tables will use `Mapped[UUID]` and UUID-backed columns for `user_id`:
29+
30+
- `user_profiles`
31+
- `user_security`
32+
- `user_settings`
33+
- `user_contacts`
34+
- `user_addresses`
35+
- `user_verifications`
36+
- `user_sessions`
37+
38+
The authorization junction tables, permission resource link, todo owner link, and user-role junction table will retain their existing UUID Python types while gaining their missing `ForeignKey` declarations.
39+
40+
No database-level delete cascade will be introduced. This restores the relationships that existed before normalization without adding new deletion behavior.
41+
42+
### Model discovery
43+
44+
Alembic must load every normalized user model before reading `Base.metadata`. Its environment will import the user model package, which already exports all eight user-domain models, instead of importing only `UserModel` and `UserSessionModel`. This keeps migration comparison aligned with runtime metadata.
45+
46+
### Corrective migration
47+
48+
A new Alembic revision after `e73c215d7221` will repair databases where the normalization migration has already run. The existing revision will not be rewritten.
49+
50+
The corrective upgrade will:
51+
52+
1. Convert the seven normalized `user_id` columns from `VARCHAR(36)` to UUID using an explicit PostgreSQL cast.
53+
2. Recreate the six constraints dropped by `e73c215d7221`.
54+
3. Add the seven missing normalized-user constraints.
55+
56+
The migration will use deterministic constraint names. If an existing normalized `user_id` contains a malformed UUID or references a missing user, PostgreSQL will reject the migration. The migration must fail visibly rather than discard, rewrite, or detach invalid data.
57+
58+
The downgrade will drop the 13 constraints added by the corrective revision and convert the seven normalized `user_id` columns back to `VARCHAR(36)`, returning the schema to the exact state represented by `e73c215d7221`.
59+
60+
### Runtime data flow
61+
62+
After mapper configuration succeeds, seeding keeps its current transaction flow: seed authorization resources, roles, permissions, and junction records; then seed users, normalized profile data, and role assignments. The fix changes schema metadata and integrity enforcement only. Seed definitions and idempotency behavior remain unchanged.
63+
64+
## Testing and Verification
65+
66+
Regression tests will import the complete model graph and assert that:
67+
68+
- `configure_mappers()` completes without `NoForeignKeysError`.
69+
- All 13 child columns expose the expected foreign-key target in `Base.metadata`.
70+
- The seven normalized `user_id` columns use UUID rather than string types.
71+
72+
The implementation will then run focused tests, the full test suite, Ruff, and Alembic migration checks. Against the configured local PostgreSQL service, verification will upgrade through the corrective revision and run `make seed`. A disposable database or reversible upgrade/downgrade cycle will be used for migration verification so application data is not destroyed.
73+
74+
## Scope
75+
76+
This change restores all affected foreign keys and corrects the normalized user identifier types. It does not change domain entities, repository APIs, seed contents, authorization policy definitions, indexes, uniqueness rules, or delete semantics.

0 commit comments

Comments
 (0)