软件版本:Alembic 1.0.5,SQLAlchemy 1.2.14,MySQL 5.7,Python 3.6.7
我正在尝试使用alembic保持MySQL数据库架构和Python ORM表示一致。
我看到的问题是,迁移总是具有多余的drop并为外键创建命令。似乎自动生成将某些东西视为不同,但实际上它们是相同的。
重复调用命令:
alembic revision --autogenerate
alembic upgrade head
...将产生相同的drop并创建命令。
记录到标准输出显示类似(例如)的内容:
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'),
)
是否可以做些什么使数据库中的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
答案 0 :(得分:1)
所以,尽管这样的问题很老,让我有了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 的更多信息。
我发现easy选项有效,并且进行了以下更改:
# 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"),