使用SQLAlchemy和Alembic检查数据库中是否存在表列

时间:2014-06-06 13:04:02

标签: postgresql sqlalchemy alembic

我正在使用Alembic作为迁移工具,我在已经更新的数据库上启动以下伪脚本(没有Alembic的修订条目,数据库模式只是最新的)。

revision = '1067fd2d11c8'
down_revision = None

from alembic import op
import sqlalchemy as sa


def upgrade():
    op.add_column('box', sa.Column('has_data', sa.Boolean, server_default='0'))


def downgrade():
    pass

只有PostgreSQL背后才会出现以下错误(对MySQL来说一切都很好):

INFO  [alembic.migration] Context impl PostgresqlImpl.
INFO  [alembic.migration] Will assume transactional DDL.
INFO  [root] (ProgrammingError) ERREUR:  la colonne « has_data » de la relation « box » existe déjà

最后一行表示列has_data已存在。

我想在op.add_column之前检查列是否存在。

2 个答案:

答案 0 :(得分:4)

最简单的答案是不要试图这样做。相反,使您的Alembic迁移代表数据库的完整布局。然后,您所做的任何迁移都将基于对现有数据库的更改。

如果您已有数据库,要进行初始迁移,请暂时指向空数据库并运行alembic revision --autogenerate -m "base"。然后,返回实际的数据库并运行alembic stamp head,表示数据库的当前状态由最新的迁移表示,而不实际运行它。

如果您因某些原因不想这样做,您可以选择不使用--autogenerate,而是生成您填写所需操作的空修订。 Alembic不会阻止你做这件事,它不太方便。

答案 1 :(得分:0)

不幸的是,在这样的情况下,我们有多个具有不同架构的版本,所有这些版本都需要迁移到单个代码库。尚无任何迁移,任何数据库中都没有标记版本。因此,第一次迁移将进行这些条件检查。第一次迁移后,一切都会处于已知状态,我可以避免此类黑客攻击。

因此,我在迁移中添加了此标签(信用属于http://www.derstappen-it.de/tech-blog/sqlalchemie-alembic-check-if-table-has-column):

from alembic import op
from sqlalchemy import engine_from_config
from sqlalchemy.engine import reflection

def _table_has_column(table, column):
    config = op.get_context().config
    engine = engine_from_config(
        config.get_section(config.config_ini_section), prefix='sqlalchemy.')
    insp = reflection.Inspector.from_engine(engine)
    has_column = False
    for col in insp.get_columns(table):
        if column not in col['name']:
            continue
        has_column = True
    return has_column

我的升级功能具有以下检查功能(请注意,我设置了一个批处理标志集,该标志添加了with op.batch_alter_table行,这在大多数设置中可能不是这样的:

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    with op.batch_alter_table('mytable', schema=None) as batch_op:
        if not _table_has_column('mytable', 'mycol'):
            batch_op.add_column(sa.Column('mycol', sa.Integer(), nullable=True))
        if not _table_has_column('mytable', 'mycol2'):
            batch_op.add_column(sa.Column('mycol2', sa.Integer(), nullable=True))