【问题标题】:`alembic revision --autogenerate` produces redundant foreign key migrations`alembic revision --autogenerate` 产生冗余外键迁移
【发布时间】:2019-04-30 14:26:46
【问题描述】:

软件版本:alembic 1.0.5、SQLAlchemy 1.2.14、MySQL 5.7、Python 3.6.7

我正在尝试使用 alembic 来保持 MySQL 数据库架构和 Python ORM 表示的同步。

我看到的问题是迁移总是有多余的删除并为外键创建命令。似乎 autogenerate 认为某些东西有所不同,但它们实际上是相同的。

关于命令的重复调用:

alembic revision --autogenerate 
alembic upgrade head

...将产生相同的 drop 和 create 命令。

记录到标准输出显示类似(例如):

INFO  [alembic.autogenerate.compare] Detected removed foreign key (t1_id)(id) on table table_two
INFO  [alembic.autogenerate.compare] Detected added foreign key (t1_id)(id) on table test_fktdb.table_two

迁移脚本有:

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_constraint('fk_table1', 'table_two', type_='foreignkey')
    op.create_foreign_key('fk_table1', 'table_two', 'table_one', ['t1_id'], ['id'], source_schema='test_fktdb', referent_schema='test_fktdb')
    # ### end Alembic commands ###


def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_constraint('fk_table1', 'table_two', schema='test_fktdb', type_='foreignkey')
    op.create_foreign_key('fk_table1', 'table_two', 'table_one', ['t1_id'], ['id'])
    # ### end Alembic commands ###

这个问题可以复制,我做了一个最小的例子(https://github.com/sqlalchemy/alembic/files/2625781/FK_test.tar.gz 上的 tar.gz)。示例中的 ORM 类似于:

[...import and bobs...]

class TableOne(Base):
    """Class representing a table with an id."""
    __tablename__ = "table_one"

    id = Column(UNSIGNED_INTEGER, nullable=False, autoincrement=True, primary_key=True)

    __table_args__ = (
        dict(mysql_engine='InnoDB'),
    )


class TableTwo(Base):
    """A table representing records with a foreign key link to table one."""
    __tablename__ = "table_two"

    id = Column(UNSIGNED_INTEGER, nullable=False, autoincrement=True, primary_key=True)
    t1_id = Column(UNSIGNED_INTEGER, nullable=False)

    __table_args__ = (
        ForeignKeyConstraint(["t1_id"], ["test_fktdb.table_one.id"], name="fk_table1"),
        dict(mysql_engine='InnoDB'),
    )

有什么办法可以让 alembic “看到”数据库中的 FK 与 ORM 中的 FK 相同?例如,通过env.py 应用一些配置?

我环顾四周寻找这个问题,并在 alembic GitHub 中发现了一些旧问题(请参阅 [1]、[2]、[3])。有解决方案的问题似乎与 postgres 数据库和公开的模式有关。我不确定这是否适用于这种情况,因为我使用的是 MySQL;公共 postgres 模式的相关文档在这里:https://docs.sqlalchemy.org/en/latest/dialects/postgresql.html#remote-schema-table-introspection-and-postgresql-search-path

我现在已将自己的问题添加到 alembic GitHub 存储库:https://github.com/sqlalchemy/alembic/issues/519


alembic 问题跟踪器中的已关闭问题,显示出类似的症状,但其解决方案不适用(据我所知):

[1]https://github.com/sqlalchemy/alembic/issues/444

[2]https://github.com/sqlalchemy/alembic/issues/398

[3]https://github.com/sqlalchemy/alembic/issues/293

【问题讨论】:

    标签: python mysql sqlalchemy alembic


    【解决方案1】:

    所以,虽然这个 SO 问题很老,并且让我获得了 Tumbleweed 徽章,但我认为回答它并关闭它会很好。我在 GitHub 上从包维护者 Mike Bayer 那里得到了很好的回答:

    好的,事情就是这样。您正在使用数据库 URL 中的“test_fktdb”作为默认架构进行连接。这意味着,alembic 将在该架构中查找您的表,当它找到外键时,它将将该 FK 中的“schema_name”字段视为空,因为这是默认架构。所以它与您在元数据中的内容不匹配。此外,您没有将“include_schemas=True”添加到环境中,因此当您的 ORM 模型中包含“schema='test_fktdb'”时,您肯定不会得到合理的结果。

    您可以进入两个通用世界来解决此问题。

    • 简单的。完全从您的表/元数据/外键中取出“模式”。那么默认情况下 test_fktdb 中的所有内容都可以正常工作,并且所有内容都匹配。

    • 很难。您需要在 URL 上连接到不同的数据库,然后在您的环境中设置 include_schemas=True,您可能还需要一个合理的 include_object() 方案,以便它不会读取所有其他数据库,设置 version_table_schema='test_fktdb' ,那么也可以:

    env.py:
    
    SCHEMA_NAME = "NOT_test_fktdb"
    
        def include_object(object, name, type_, reflected, compare_to):
            if (type_ == "table"):
                return object.schema == "test_fktdb"
    
            else:
                return True
    
        with connectable.connect() as connection:
            context.configure(
                connection=connection,
                target_metadata=target_metadata,
                compare_type=True,
                compare_server_default=True,
                include_schemas=True,
                version_table_schema="test_schema",
                include_object=include_object
            )
    
           # ...
    

    “模式”逻辑必须严重依赖于“默认”模式的概念,即“默认”模式是一个空白字符串,因此当您混淆默认模式也存在时,它会使事情变得混乱。

    GitHub 上有更多内容https://github.com/sqlalchemy/alembic/issues/519

    我发现简单的选项有效,我做了以下更改:

    # instead of [...]:
    # declarative_base(metadata=sqlalchemy.MetaData(schema=test_fktdb.SCHEMA_NAME))
    Base = sqlalchemy.ext.declarative.declarative_base()
    
    # instead of [...]:
    # ForeignKeyConstraint(["t1_id"], ["test_fktdb.table_one.id"], name="fk_table1"),
    ForeignKeyConstraint(["t1_id"], ["table_one.id"], name="fk_table1"),
    

    【讨论】:

      猜你喜欢
      • 2013-03-17
      • 1970-01-01
      • 2013-05-25
      • 2022-11-10
      • 1970-01-01
      • 2017-01-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多