diff --git a/backend/alembic/versions/f0e9d8c7b6a5_cascade_ondelete_all_env_fks.py b/backend/alembic/versions/f0e9d8c7b6a5_cascade_ondelete_all_env_fks.py index aab44b85..205671bf 100644 --- a/backend/alembic/versions/f0e9d8c7b6a5_cascade_ondelete_all_env_fks.py +++ b/backend/alembic/versions/f0e9d8c7b6a5_cascade_ondelete_all_env_fks.py @@ -1,17 +1,28 @@ """cascade ondelete for all remaining environments FK references Revision ID: f0e9d8c7b6a5 -Revises: e1f2a3b4c5d6, e5f4d3c2b1a +Revises: a5b6c7d8e9f0, e5f4d3c2b1a Create Date: 2026-05-21 19:00:00.000000 +@RATIONALE The dataset_review_sessions table is created at runtime by + init_db() → Base.metadata.create_all(), not by any Alembic migration. + On a fresh database, it does not exist when Alembic runs (entrypoint + runs alembic upgrade head BEFORE the backend starts). The model already + defines the FK with ondelete='CASCADE', so on fresh databases the FK is + correct. This migration only needs to alter the FK on databases that + were upgraded from before the FK had CASCADE. + + Guard: skip dataset_review_sessions FK if the table doesn't exist. """ from collections.abc import Sequence from alembic import op +from sqlalchemy import inspect + # revision identifiers, used by Alembic. revision: str = 'f0e9d8c7b6a5' -down_revision: str | Sequence[str] | None = ('e1f2a3b4c5d6', 'e5f4d3c2b1a') +down_revision: str | Sequence[str] | None = ('a5b6c7d8e9f0', 'e5f4d3c2b1a') branch_labels: str | Sequence[str] | None = None depends_on: str | Sequence[str] | None = None @@ -25,9 +36,18 @@ FK_DEFS = [ ] +def _table_exists(table: str) -> bool: + """Check if a table exists in the current database connection.""" + conn = op.get_bind() + inspector = inspect(conn) + return inspector.has_table(table) + + def upgrade() -> None: """Upgrade schema.""" for table, constraint_name, column in FK_DEFS: + if not _table_exists(table): + continue op.drop_constraint(constraint_name, table, type_='foreignkey') op.create_foreign_key( constraint_name, @@ -42,6 +62,8 @@ def upgrade() -> None: def downgrade() -> None: """Downgrade schema.""" for table, constraint_name, column in reversed(FK_DEFS): + if not _table_exists(table): + continue op.drop_constraint(constraint_name, table, type_='foreignkey') op.create_foreign_key( constraint_name,