如何使用sqlalchemy-migrate编写alter column name migration?

时间:2011-10-05 10:31:46

标签: python sqlite postgresql heroku 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')

在我的开发数据库(sqlite)上运行migrate.py test,升级和降级也是如此。但是当它在Heroku(使用PostgreSQL 8.3)上部署到我的测试环境时,我会在尝试升级时得到一个跟踪。要点是这个消息:

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

然后我尝试在升级方法中使用users.c.user_id。这在两种环境中都失败了。:

AttributeError: user_id

我现在使用的解决方法是这个脚本:

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

已经建议将模型复制粘贴到sqlalchemy-migrate脚本。但是这个额外的重复对我来说有点太多了。任何人都知道应该怎么做。假设这是一个错误,我想了解如何干预解决方法。

3 个答案:

答案 0 :(得分:13)

事实证明,这是一个比我希望的更干的解决方案。内省!像这样:

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

像魅力一样!

答案 1 :(得分:8)

这个也有效:

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

答案 2 :(得分:1)

我敢打赌,它无法生成任何SQL,因为您的元数据引用混淆了。您似乎在Table类中使用了两个不同的元数据对象,这确实不太好。你只需要一个。元数据跟踪对象的陈旧性,是否需要发出对象更新查询,外键约束等,并且需要知道所有表和关系。

更改为使用单个MetaData对象,并将echo=True传递给sqlalchemy.create_engine调用,它将打印出它正在使用的SQL查询到标准输出。在以Postgres的相同角色(用户)身份登录时尝试自己执行该查询。您可能会发现这是一个简单的权限问题。

关于复制粘贴:我认为Django有一个很好的约定,即在自己的模块中放置Table和声明性类并导入它们。但是,因为您必须将MetaData对象传递给Table工厂,这会使问题复杂化。您可以使用单例/全局元数据对象,也可以只转换为声明性。

有一段时间我选择实现一个参数函数,它返回给定元数据的Table个对象并缓存结果 - 实际上是实现单例模型类。然后我觉得这很愚蠢,转而陈述。