2011-10-05 11 views
5

Sto tentando di modificare il nome di una colonna. Primo tentativo è stato con questo script:Come scrivere le migrazioni dei nomi delle colonne con sqlalchemy-migrate?

meta = MetaData() 

users = Table('users', meta, 
    Column('id', Integer, primary_key=True), 
    Column('name', String(50), unique=True), 
    Column('email', String(120), unique=True) 
    ) 

def upgrade(migrate_engine): 
    meta.bind = migrate_engine 
    users.c.id.alter(name='id') 

def downgrade(migrate_engine): 
    meta.bind = migrate_engine 
    users.c.id.alter(name='user_id') 

esecuzione migrate.py test sul mio database dev (SQLite) funziona e così fa il potenziamento e il declassamento. Ma quando lo distribuisco nel mio ambiente di test su Heroku (dove viene usato PostgreSQL 8.3) ottengo una traccia quando provo ad aggiornare. Gist è questo messaggio:

sqlalchemy.exc.ProgrammingError: (ProgrammingError) column "id" does not exist 

Allora ho provato ad utilizzare users.c.user_id nel metodo di aggiornamento. Che non riesce in entrambi gli ambienti .:

AttributeError: user_id 

La soluzione che sto utilizzando ora è questo script:

meta_old = MetaData() 
meta_new = MetaData() 

users_old = Table('users', meta_old, 
    Column('user_id', Integer, primary_key=True), 
    Column('name', String(50), unique=True), 
    Column('email', String(120), unique=True) 
    ) 

users_new = Table('users', meta_new, 
    Column('id', Integer, primary_key=True), 
    Column('name', String(50), unique=True), 
    Column('email', String(120), unique=True) 
    ) 

def upgrade(migrate_engine): 
    meta_old.bind = migrate_engine 
    users_old.c.user_id.alter(name='id') 

def downgrade(migrate_engine): 
    meta_new.bind = migrate_engine 
    users_new.c.id.alter(name='user_id') 

E 'già consigliato l' copiare e incollare il modello per gli script SQLAlchemy-migrare. Ma questa duplicazione extra diventa un po 'troppo per me. Qualcuno sa come dovrebbe essere fatto. Supponendo che sia un bug, mi piacerebbe avere dei suggerimenti su come ASCIUGARE la soluzione alternativa.

risposta

12

Si scopre che c'è una soluzione anche ASCIUTTA: per questo, di quanto avessi sperato. Introspezione! Così:

def upgrade(migrate_engine): 
    meta = MetaData(bind=migrate_engine) 
    users = Table('users', meta, autoload=True) 
    users.c.user_id.alter(name='id') 

def downgrade(migrate_engine): 
    meta = MetaData(bind=migrate_engine) 
    users = Table('users', meta, autoload=True) 
    users.c.id.alter(name='user_id') 

Funziona come un fascino!

+2

Bello, anche se sono diffidente nell'usare autoload = True nelle migrazioni dello schema. Solo un anticipo sul futuro, ricorda di applicare le modifiche al contrario nel downgrade! Se non lo fai, avrai (probabilmente) molti errori! –

1

Scommetto che non può generare alcun SQL perché i riferimenti ai metadati si confondono. Sembra che tu stia utilizzando due diversi oggetti di metadati nelle tue classi Table e questo non è davvero positivo. Ne hai solo bisogno. I metadati tracciano la stagnazione degli oggetti, se è necessario emettere query per gli aggiornamenti degli oggetti, i vincoli delle chiavi esterne, ecc. E devono conoscere tutte le tabelle e le relazioni.

Cambio di utilizzare un unico MetaData oggetto, e passare echo=True al sqlalchemy.create_engine chiamata e stamperà la query SQL che si sta usando standard output. Prova a eseguire la query tu stesso mentre sei loggato come lo stesso ruolo (utente) a Postgres. Potresti scoprire che si tratta di un problema di autorizzazioni semplice.

Riguardo al copia-incolla: penso che Django abbia una buona convenzione di collocare Table e classi dichiarative nel proprio modulo e importarle. Tuttavia, poiché è necessario passare un oggetto MetaData allo stabilimento Table, ciò complica le cose. È possibile utilizzare un oggetto metadati globale/singleton o semplicemente convertirlo in dichiarativo.

Per un po 'ho scelto di implementare funzioni a un argomento che restituivano oggetti Table dati metadati e memorizzavano nella cache il risultato - in effetti implementando una classe modello singleton. Poi ho deciso che era sciocco e passò a dichiarativo.

+0

mi rendo conto che non è buona norma usare due oggetti di metadati. Ma questa è solo la soluzione per far funzionare lo script. È il primo script che non funziona. La traccia contiene l'istruzione SQL e sta tentando di alterare la colonna con il nome sbagliato. Buono a sapersi che echo = Vero, sarà utile! – PEZ

+0

Informazioni su non copia-incolla. È il consiglio di sqlalchemy-migrate USer Guide: http://packages.python.org/sqlalchemy-migrate/versioning.html # writing-scripts-with-consistent-behavior Non ho capito come utilizzare dichiarative insieme a sqlalchemy-migrate. È possibile? – PEZ

+0

Non l'ho provato. In bocca al lupo! – wberry

2

Questo funziona anche:

from alembic import op 
.... 
def upgrade(migrate_engine): 
    op.alter_column('users', 'user_id', new_column_name='id') 

def downgrade(migrate_engine): 
    op.alter_column('users', 'id', new_column_name='user_id') 
Problemi correlati