2011-10-05 7 views
5

Estoy intentando modificar un nombre de columna. Primer intento fue con este script:¿Cómo escribir las migraciones de nombre de columna alternas 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') 

Correr migrate.py test en mi base de datos dev (sqlite) que funciona y lo mismo ocurre con cambio de clase. Pero al implementarlo en mi entorno de prueba en Heroku (donde se usa PostgreSQL 8.3) obtengo un seguimiento cuando intento actualizarlo. Síntesis es este mensaje:

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

Luego trató de utilizar users.c.user_id en el método de actualización. Eso no funciona tanto en entornos .:

AttributeError: user_id 

La solución que estoy usando ahora es este 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') 

Es una práctica ya se recomienda copiar y pegar el modelo para las secuencias de comandos SQLAlchemy-migrar. Pero estas duplicaciones adicionales son demasiado para mí. Alguien sabe cómo debe hacerse esto. Asumiendo que es un error, me gustaría recibir sugerencias sobre cómo SECAR la solución temporal.

Respuesta

12

Resulta que hay una solución incluso DRY: er para esto de lo que esperaba. ¡Introspección! Como tal:

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') 

Works like a charm!

+2

Bueno, aunque desconfío de usar autoload = True en las migraciones de esquema. ¡Solo un aviso para el futuro, recuerde aplicar sus cambios al revés en la versión anterior! Si no lo haces, (probablemente) tendrás muchos errores. –

1

Apuesto a que no puede generar ningún SQL porque sus referencias de metadatos se están confundiendo. Parece que está utilizando dos objetos de metadatos diferentes en sus clases Table, y eso realmente no es bueno. Solo necesitas uno Los metadatos rastrean la obsolescencia de los objetos, ya sea que necesiten emitir consultas para actualizaciones de objetos, restricciones de claves externas, etc., y necesita conocer todas sus tablas y relaciones.

Cambie para usar un solo objeto MetaData, y pase echo=True a su llamada sqlalchemy.create_engine e imprimirá la consulta SQL que está utilizando para la salida estándar. Intente ejecutar esa consulta usted mismo mientras está conectado como el mismo rol (usuario) a Postgres. Puede encontrar que es un simple problema de permisos.

En cuanto a copiar y pegar: Creo que Django tiene una buena convención de colocar Table y clases declarativas en su propio módulo e importarlas. Sin embargo, debido a que tiene que pasar un objeto MetaData a la fábrica Table, eso complica las cosas. Puede usar un objeto de metadatos único/global, o simplemente convertir a declarativo.

Por un tiempo elegí implementar funciones de un argumento que devolvieron los objetos Table dados un metadato y almacenaron en caché el resultado, implementando de hecho una clase de modelo singleton. Entonces decidí que era una tontería y cambié a declarativo.

+0

me doy cuenta que no es una buena práctica utilizar dos objetos de metadatos. Pero esa es solo la solución para hacer que el script funcione. Es el primer script que no funciona. La traza contiene la instrucción SQL e intenta alterar la columna por el nombre incorrecto. Es bueno saber sobre ese eco = ¡Cierto, será útil! – PEZ

+0

Acerca de no copiar y pegar. Es un consejo de la guía de usuario sqlalchemy-migrate: http://packages.python.org/sqlalchemy-migrate/versioning.html # writing-scripts-with-consistent-behavior No he descubierto cómo usar declarativo junto con sqlalchemy-migrate. ¿Es posible? – PEZ

+0

No lo he intentado. ¡Buena suerte! – wberry

2

Este también funciona:

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') 
Cuestiones relacionadas