From 5838f70ea40b417b9375445af3e53a91d5c8055b Mon Sep 17 00:00:00 2001 From: Tudor Date: Wed, 25 Mar 2026 14:30:09 +0000 Subject: [PATCH] fix(migration): ALTER TABLE to add new columns on existing supplementary tables create_all() only creates missing tables; it won't modify tables that already exist from an older schema version. Add _apply_schema_alterations() which runs idempotent ADD COLUMN IF NOT EXISTS statements after every migration so supplementary tables (like ofsted_inspections) gain new columns without dropping their existing data. Co-Authored-By: Claude Sonnet 4.6 --- backend/migration.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/backend/migration.py b/backend/migration.py index 630155c..e64147e 100644 --- a/backend/migration.py +++ b/backend/migration.py @@ -404,6 +404,35 @@ def migrate_data(df: pd.DataFrame, geocode: bool = False, geocode_cache: dict = print("\nMigration complete!") +def _apply_schema_alterations(): + """ + Add new columns to existing tables using ALTER TABLE … ADD COLUMN IF NOT EXISTS. + Safe to run on every migration — no-ops if the column already exists. + Add entries here whenever models.py gains new columns on an existing table. + """ + alterations = [ + # v4: Ofsted Report Card columns + "ALTER TABLE ofsted_inspections ADD COLUMN IF NOT EXISTS framework VARCHAR(20)", + "ALTER TABLE ofsted_inspections ADD COLUMN IF NOT EXISTS rc_safeguarding_met BOOLEAN", + "ALTER TABLE ofsted_inspections ADD COLUMN IF NOT EXISTS rc_inclusion INTEGER", + "ALTER TABLE ofsted_inspections ADD COLUMN IF NOT EXISTS rc_curriculum_teaching INTEGER", + "ALTER TABLE ofsted_inspections ADD COLUMN IF NOT EXISTS rc_achievement INTEGER", + "ALTER TABLE ofsted_inspections ADD COLUMN IF NOT EXISTS rc_attendance_behaviour INTEGER", + "ALTER TABLE ofsted_inspections ADD COLUMN IF NOT EXISTS rc_personal_development INTEGER", + "ALTER TABLE ofsted_inspections ADD COLUMN IF NOT EXISTS rc_leadership_governance INTEGER", + "ALTER TABLE ofsted_inspections ADD COLUMN IF NOT EXISTS rc_early_years INTEGER", + "ALTER TABLE ofsted_inspections ADD COLUMN IF NOT EXISTS rc_sixth_form INTEGER", + ] + from sqlalchemy import text as sa_text + with engine.connect() as conn: + for stmt in alterations: + try: + conn.execute(sa_text(stmt)) + except Exception as e: + print(f" Warning: alteration skipped ({e})") + conn.commit() + + def run_full_migration(geocode: bool = False) -> bool: """ Run a complete migration: drop all tables and reimport from CSV. @@ -443,6 +472,13 @@ def run_full_migration(geocode: bool = False) -> bool: print("Creating all tables...") Base.metadata.create_all(bind=engine) + # ALTER existing supplementary tables to add any new columns. + # create_all() only creates missing tables; it won't add columns to tables + # that already exist from an older schema version. These statements are + # idempotent (IF NOT EXISTS) so they're safe to run on every migration. + print("Applying column additions to supplementary tables...") + _apply_schema_alterations() + print("\nLoading CSV data...") df = load_csv_data(settings.data_dir)