6

La migrazione Alembic estera autoreferenziale per un database SQLite:Alambicco SQLite ALTER TABLE con chiave

def upgrade(): 
    with op.batch_alter_table('my_table') as batch_op: 
     batch_op.add_column(sa.Column('parent_id', sa.String(24))) 
     batch_op.create_foreign_key('parent_constraint', 'my_table', ['parent_id'], ['id']) 

che dovrebbe creare una chiave esterna parent_id riferimento id della stessa tabella my_table, crea un riferimento ad un tavolo chiamato _alembic_batch_temp:

CREATE TABLE "my_table" (
    id VARCHAR(24) NOT NULL, 
    parent_id VARCHAR(24), 
    PRIMARY KEY (id), 
    CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES _alembic_batch_temp (id) 
) 

Come creare vincoli autoreferenziali quando si modifica una tabella?

risposta

5

Dopo alcune ricerche ho scoperto che il problema qui è il modo in cui Alembic esegue la migrazione batch. In breve, nella versione corrente (0.7.6) di Alembic non è possibile creare una relazione con la propria identità tramite la migrazione.

  1. Come descritto nel Alambicco documentation, per fare la migrazione, nuova tabella viene creato con un nome temporaneo e modifiche dalla tabella codice alter . In questo caso:

    CREATE TABLE _alembic_batch_temp (
        id VARCHAR(24) NOT NULL, 
        parent_id VARCHAR(24), 
        PRIMARY KEY (id), 
        CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES _alembic_batch_temp (id) 
    ) 
    
  2. La tabella viene riempita con i dati dalla vecchia tabella:

    INSERT INTO _alembic_batch_temp (id) SELECT id FROM my_table; 
    
  3. Poi la vecchia tabella viene rimosso:

    DROP TABLE my_table; 
    
  4. Infine neo la tabella creata viene rinominata con il nome proprio:

    ALTER TABLE _alembic_batch_temp RENAME TO my_table; 
    

Il problema con questo modo di fare le cose è già visibile nel primo snippet di codice. La chiave esterna appena creata fa riferimento alla tabella temporanea e una volta creata non può essere modificata a causa di restrizioni in SQLite. Così, dopo la ridenominazione del tavolo si finisce con la tabella che hai fornito:

CREATE TABLE "my_table" ( # new name 
    id VARCHAR(24) NOT NULL, 
    parent_id VARCHAR(24), 
    PRIMARY KEY (id), 
    CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES _alembic_batch_temp (id) # old reference 
) 

Per evitare questa situazione è possibile creare la migrazione in batch manualmente:

  1. Rinominare il vecchio tavolo a qualche nome temporaneo :

    ALTER TABLE my_table RENAME TO migration_temp_table; 
    
  2. Creare nuova tabella con nome proprio e di riferimento corretto:

    CREATE TABLE my_table (
        id VARCHAR(24) NOT NULL, 
        parent_id VARCHAR(24), 
        PRIMARY KEY (id), 
        CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES my_table (id) 
    ) 
    
  3. copiare i dati:

    INSERT INTO my_table (id) SELECT id FROM migration_temp_table; 
    
  4. Rimuovere la vecchia tabella:

    DROP TABLE migration_temp_table; 
    
+2

A quanto pare, la tua soluzione non è più necessario, almeno da SQLite 3.13.0 (.Net 1.0.102.0). Ho testato questo scenario esatto (il primo) con questa versione e funziona come previsto: l'auto riferimento è rinominato con la tabella. Ho anche provato con un errore di cancellazione CASCADE ei miei dati sono rimasti dove dovevano essere :) – Yaurthek