`alembic revision--autogenerate`生成冗余的外键迁移

a8jjtwal  于 2021-06-17  发布在  Mysql
关注(0)|答案(1)|浏览(522)

软件版本: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

…将生成相同的拖放和创建命令。
登录到stdout显示如下内容(例如):

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 ###

这个问题可以复制,我已经做了一个最小的例子(tar.gz)https://github.com/sqlalchemy/alembic/files/2625781/fk_test.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 例如?
我环顾了一下这个问题,在alembicgithub中发现了一些老问题(参见[1]、[2]、[3])。有解决方案的问题似乎涉及postgres数据库和公共模式。我不确定这是否适用于这种情况,因为我使用的是mysql;公共postgres模式的相关文档如下:https://docs.sqlalchemy.org/en/latest/dialects/postgresql.html#remote-模式表自省和postgresql搜索路径
我现在将自己的问题添加到alembic github回购中:https://github.com/sqlalchemy/alembic/issues/519
alembic issue tracker中的已解决问题,显示类似症状,但其解决方案不适用(据我所知):
[1] https://github.com/sqlalchemy/alembic/issues/444
[2] https://github.com/sqlalchemy/alembic/issues/398
[3] https://github.com/sqlalchemy/alembic/issues/293

gupuwyp2

gupuwyp21#

所以,虽然这个问题很老了,给了我风滚草徽章,但我想回答它并结束它会很好。在github上,包维护人员mike bayer给了我一个很好的答案:
好吧,事情是这样的。您正在连接数据库url中的“test\u fktdb”作为默认架构。这意味着,alembic将在该模式中查找表,当它找到外键时,它将看到fk中的“schema\u name”字段为空,因为这是默认模式。所以它与元数据中的内容不匹配。另外,您没有将“include\u schemas=true”添加到环境中,因此当您的orm模型中包含“schema='test\u fktdb'”时,您肯定不会得到合理的结果。
你可以进入两个世界来解决这个问题。
简单一点。完全从表/元数据/外键中去掉“schema”。然后在test\u fktdb中所有内容都作为默认值工作,并且所有内容都匹配。
很难。您需要连接到url上的另一个数据库,然后在您的环境中设置include\u schemas=true,您可能还需要一个合理的include\u object()方案,以便它不会读入所有其他数据库,设置version\u table\u schema='test\u 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
        )

       # ...

“schema”逻辑必然严重依赖于“default”schema是一个空白字符串的概念,因此当您混淆了默认schema的存在时,它会混淆事情。
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"),

相关问题