【问题标题】:SqlAlchemy alembic migration files dont use the connection settings in env.py?SqlAlchemy alembic 迁移文件不使用 env.py 中的连接设置?
【发布时间】:2019-02-10 18:28:35
【问题描述】:

我有一个适用于名为 tenant_schema 的通用架构的迁移。 在env.pyrun_migrations_online 函数中,我为tenant_schema 设置了一个schema_translate_map。

我希望 sqlalchemy 将此迁移操作转换为在所需架构上运行,但它似乎尝试使用架构 tenant_schema 运行 sql 查询。

任何想法如何解决它?

示例

迁移文件中的升级功能:

2018-09-05_17-28_247f3546088f_add_foo_column.py

def upgrade():
    op.add_column('derived_table', sa.Column('foo', sa.BigInteger(), nullable=True), 
                  schema='tenant_schema')

run_migrations_online 函数:

env.py

schema = 'other_name'  # normally i get the name as an argument from alembic
def run_migrations_online():
    connectable = create_engine(get_url(), echo=True)

    with connectable.connect() as connection:
        # setting up the translation map
        conn = connection.execution_options(schema_translate_map={'tenant_schema': schema})
        context.configure(
            connection=conn,
            target_metadata=target_metadata,
            include_schemas=True,
            version_table_schema=schema,
            include_object=include_object,
        )
        with context.begin_transaction():
            context.run_migrations()

异常(完整回溯太长且信息量不足):

sqlalchemy.exc.ProgrammingError: (psycopg2.ProgrammingError) schema "tenant_schema" does not exist
 [SQL: 'ALTER TABLE tenant_schema.derived_table ADD COLUMN foo BIGINT'] 
(Background on this error at: http://sqlalche.me/e/f405)

如您所见,它尝试执行 ALTER TABLE tenant_schema.derived_table 而不是所需的 ALTER TABLE other_name.derived_table

【问题讨论】:

    标签: python orm sqlalchemy alembic


    【解决方案1】:

    问题

    来自SQLAlchemy docs 关于schema_translate_map(强调我的):

    该功能仅在模式名称直接从表或序列的名称派生的情况下生效; 它不会影响直接传递字符串模式名称的方法

    由于所有架构都直接在 alembic 迁移操作中传递,因此不考虑 schema_translate_map

    解决方案

    你可能需要的是:

    1. 使用 alembic 挂钩来调整架构添加到迁移的方式,使其不是文字字符串,而是在某些全局上下文中查找(例如,渲染 os.environ['TENANT_SCHEMA'] 而不是文字字符串 'tenant_schema')。

      可能正确的挂钩位置是覆盖渲染函数,请参阅docs 中的示例。不幸的是,我无法为此显示任何代码,因为我自己没有尝试过。

      或者,您可以尝试注册您的自定义比较器,该比较器将在 alembic 之后运行,实际上不会比较任何内容,而是将 alembic 生成的操作中的 schema 属性替换为自定义字符串子类:

      from alembic.autogenerate import comparators
      
      class FakeSchema(str):
          def __repr__(self):
              return "os.environ['TENANT_SCHEMA']"
      
      @comparators.dispatch_for('schema')
      def tweak_schema(autogen_context, upgrade_ops, schemas):
          for op in upgrade_ops.ops:
              if getattr(op, 'schema', None) == 'tenant_schema':
                  op.schema = FakeSchema(op.schema)
                  autogen_context.imports.add('import os')  # for os.environ
      

      您可以阅读比较器功能in alembic docs

    2. 将该全局上下文中的架构名称设置为运行迁移时所需的值(在此示例中,将 TENANT_SCHEMA 环境变量传递给 alembic 或将其添加到 env.py 中的 os.environ 中)。

    【讨论】:

    • 自定义比较器对我不起作用,我是否必须将其传递给context.configure(例如process_revision_directives)?
    • 或者我必须编写自己的自定义自动生成脚本吗?
    • 不,您只需将其添加到 env.py,但您需要重新生成或手动修改现有迁移才能以相同的方式工作。
    • 但我应该如何将它添加到上下文中?
    • 我最终使用了重写器功能,但使用了您的FakeSchema。非常感谢您。享受 50 :)
    猜你喜欢
    • 2018-08-24
    • 1970-01-01
    • 2014-12-02
    • 2018-09-09
    • 2018-06-29
    • 2012-11-20
    • 2018-07-08
    • 2017-09-07
    • 2013-06-16
    相关资源
    最近更新 更多