如何使用 sqlalchemy-migrate 编写更改列名迁移?

发布于 2024-12-08 16:06:56 字数 1580 浏览 1 评论 0原文

我正在尝试更改列名称。第一次尝试是使用此脚本:

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 脚本中。但这种额外的重复对我来说有点太多了。任何人都知道这应该如何完成。假设这是一个错误,我想要一些关于如何干燥解决方法的建议。

I'm trying to alter a column name. First attempt was with this script:

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

Running migrate.py test on my dev database (sqlite) works and so does upgrading and downgrading. But when deploying it to my test environment on Heroku (where PostgreSQL 8.3 is used) I get a trace when I try to upgrade. Gist is this message:

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

I then tried to use users.c.user_idin the upgrade method. That fails in both environments.:

AttributeError: user_id

The workaround I'm using now is this 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')

It's already recommended practice to copy-paste the model to the sqlalchemy-migrate scripts. But this extra duplications gets a bit too much for me. Anyone knows how this should be done. Assuming it's a bug, I'd like suggestions on how to DRY up the workaround some.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

战皆罪 2024-12-15 16:06:56

这个也有效:

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

This one also works:

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')
你与清晨阳光 2024-12-15 16:06:56

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

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

就像魅力一样!

Turns out there's an even DRY:er solution to this than I had hoped for. Introspection! Like so:

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!

酒几许 2024-12-15 16:06:56

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

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

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

有一段时间,我选择实现单参数函数,该函数返回给定元数据的 Table 对象并缓存结果 - 实际上实现了一个单例模型类。然后我觉得这很愚蠢,于是改用声明式。

I bet that it can't generate any SQL because your metadata references are getting mixed up. You seem to be using two different metadata objects in your Table classes, and that's really not good. You only need one. The metadata tracks stale-ness of objects, whether it needs to issue queries for object updates, foreign key constraints, etc. and it needs to know about all your tables and relationships.

Change to use a single MetaData object, and pass echo=True to your sqlalchemy.create_engine call and it will print the SQL query that it's using to standard output. Try executing that query yourself while logged in as the same role (user) to Postgres. You may find that it's a simple permissions issue.

Regarding copy-pasting: I think Django has a good convention of placing Table and declarative classes in their own module and importing them. However, because you have to pass a MetaData object to the Table factory, that complicates matters. You can use a singleton/global metadata object, or just convert to declarative.

For a while I chose to implement one-argument functions that returned Table objects given a metadata and cached the result--in effect implementing a singleton model class. Then I decided that was silly and switched to declarative.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文