2010-03-11 21 views
7

Estoy usando SQLAlchemy Migrate para realizar un seguimiento de los cambios en la base de datos y estoy teniendo un problema con la eliminación de una clave externa. Tengo dos tablas, t_new es una tabla nueva, y t_exists es una tabla existente. Necesito agregar t_new, luego agregar una clave foránea a t_exists. Entonces necesito poder revertir la operación (que es donde estoy teniendo problemas).¿Cómo elimino una restricción de clave externa en SQLAlchemy?

t_new = sa.Table("new", meta.metadata, 
    sa.Column("new_id", sa.types.Integer, primary_key=True) 
) 
t_exists = sa.Table("exists", meta.metadata, 
    sa.Column("exists_id", sa.types.Integer, primary_key=True), 
    sa.Column(
     "new_id", 
     sa.types.Integer, 
     sa.ForeignKey("new.new_id", onupdate="CASCADE", ondelete="CASCADE"), 
     nullable=False 
    ) 
) 

Esto funciona bien:

t_new.create() 
t_exists.c.new_id.create() 

Pero esto no es así:

t_exists.c.new_id.drop() 
t_new.drop() 

Tratando de dejar caer la columna de clave externa da un error: 1025, "Error en el cambio de nombre de'. \ my_db_name \ # sql-1b0_2e6 'a'. \ my_db_name \ exists '(errno: 150) "

Si hago esto con SQL sin formato, puedo eliminar el f oreignar manualmente la clave y luego eliminar la columna, pero no he podido averiguar cómo eliminar la clave externa con SQLAlchemy? ¿Cómo puedo eliminar la clave externa y luego la columna?

Respuesta

5

Puede hacerlo con sqlalchemy.migrate.

Con el fin de hacer que funcione, he tenido que crear la restricción de clave externa de forma explícita en lugar de implícitamente con la columna ('FK', ForeignKey ('fk_table.field')):

Por desgracia, en vez de hacer esto:

p2 = Table('tablename', 
      metadata, 
      Column('id', Integer, primary_key=True), 
      Column('fk', ForeignKey('fk_table.field')), 
      mysql_engine='InnoDB', 
      ) 

hacer eso:

p2 = Table('tablename', 
      metadata, 
      Column('id', Integer, primary_key=True), 
      Column('fk', Integer, index=True), 
      mysql_engine='InnoDB', 
      ) 
ForeignKeyConstraint(columns=[p2.c.fk], refcolumns=[p3.c.id]).create() 

A continuación, el proceso de eliminación se ve así:

def downgrade(migrate_engine): 
    # First drop the constraint 
    ForeignKeyConstraint(columns=[p2.c.fk], refcolumns=[p3.c.id]).drop() 
    # Then drop the table 
    p2.drop() 
+2

Para cualquier persona que pueda encontrarse con esto, ForeignKeyConstraint debe importarse de 'migrate' no de' sqlalchemy'. – mjallday

2

Pude lograr esto creando una instancia de metadatos separada y usando Session.execute() para ejecutar SQL sin procesar. Idealmente, habría una solución que utiliza sqlalchemy exclusivamente, por lo que no tendría que usar soluciones específicas de MySQL. Pero a partir de ahora, no estoy al tanto de tal solución.

+3

Por curiosidad, ¿cuál fue el SQL que ejecutó? –

-1

Bueno, se puede lograr esto en sqlalchemy: sólo los drop() todas las restricciones antes de que drop() la columna (en teoría, podría tener múltiples restricciones):

def drop_column(column): 
    for fk in column.table.foreign_keys: 
     if fk.column == column: 
      print 'deleting fk ', fk 
      fk.drop() 
    column.drop() 

drop_column(t_exists.c.new_id) 
+0

Probé esto, pero obtuve un error: el objeto 'ForeignKey' no tiene ningún atributo 'drop'. Revisé los documentos pero no vi ninguna forma de hacerlo: http://www.sqlalchemy.org/docs/reference/sqlalchemy/schema.html?highlight=foreignkey#sqlalchemy.schema.ForeignKey – Travis

+0

esto es justo, pero muéstreme la documentación de SA donde Column tiene drop(): http://www.sqlalchemy.org/docs/reference/sqlalchemy/schema.html?highlight=foreignkey#sqlalchemy.schema.Column – van

+0

'SQLAlchemy-Migrate' agrega drop(), create() y otras sutilezas, pero debe instalarse por separado. Consulte http://packages.python.org/sqlalchemy-migrate/index.html – OrganicPanda

0

Creo que se puede lograr esto con SQLAlchemy- Emigrar. Tenga en cuenta que una ForeignKey está en una columna aislada. Un ForeignKeyConstraint está en el nivel de la tabla y relaciona las columnas. Si observa el objeto ForeignKey en la columna, verá que hace referencia a ForeignKeyConstraint.

No podría probar esta idea porque las dos bases de datos que uso MS SQL no son compatibles con SqlAlchemy-Migrate y sqlite no admite "alter table" para las restricciones. Obtuve SQLAlchemy para tratar de eliminar un FK mediante una caída en la restricción de referencias en una tabla sqlite, por lo que se veía bien. YMMV.

Cuestiones relacionadas