python - Alembic 迁移无法正确生成 UUID
问题描述
事实上确实如此,但我肯定做错了什么。
我有一个数据库,我在表上创建了一个 FK,在未来的迁移中,它被删除了。
所以表的create语句为:
CREATE TABLE this_table (
pk INTEGER NOT NULL,
some_other_table_pk INTEGER,
CONSTRAINT "fk_436d3a57-0ae2-5668-937d-8cbc82d15172" FOREIGN KEY(some_other_table_pk) REFERENCES some_other_table (pk)
然后我放弃约束,alembic 自动生成以下批处理操作:
def upgrade():
with op.batch_alter_table('this_table', schema=None) as batch_op:
# how does it know here which fk to drop? there are more than one???
batch_op.drop_constraint(None, type_='foreignkey')
batch_op.drop_column('some_other_table_pk')
def downgrade():
with op.batch_alter_table('this_table', schema=None) as batch_op:
batch_op.add_column(sa.Column('some_other_table_pk', sa.INTEGER(), nullable=True))
batch_op.create_foreign_key(None, 'some_other_table_pk', ['some_other_table'], ['pk'])
这导致升级头错误:
KeyError: 'fk_ad156c6d-7a24-5d8e-868e-2fb00c2cb1c9'
很明显它没有重新生成相同的密钥?!?该密钥与此表上的任何 fk 都不匹配...但是它应该如何知道要生成哪个密钥?drop_constraint 没有得到关于要删除哪个 fk 的任何信息?
我的元数据约定:
def fk_guid(constraint, table):
str_tokens = [
table.name,
] + [
element.parent.name for element in constraint.elements
] + [
element.target_fullname for element in constraint.elements
]
guid = uuid.uuid5(uuid.NAMESPACE_OID, "_".join(str_tokens))
return str(guid)
convention = {
"fk_guid": fk_guid,
"ix": "ix_%(column_0_label)s",
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(column_0_name)s",
"fk": "fk_%(fk_guid)s",
"pk": "pk_%(table_name)s",
}
metadata = MetaData(naming_convention=convention)
Base = declarative_base(metadata=metadata)
在 env.py 中:
# ...
target_metadata = models.Base.metadata
# ...
def run_migrations_offline():
url = getUrl()
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
render_as_batch=True,
dialect_opts={"paramstyle": "named"},
compare_type=True
)
with context.begin_transaction():
context.run_migrations()
def run_migrations_online():
alembic_config = config.get_section(config.config_ini_section)
connectable = create_engine(getUrl())
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
render_as_batch=True,
dialect_opts={"paramstyle": "named"},
compare_type=True
)
with context.begin_transaction():
context.run_migrations()
数据库是一个 sqlite3 文件。
编辑开始
所以我尝试手动传入要删除的约束名称:
def upgrade():
with op.batch_alter_table('this_table', schema=None) as batch_op:
batch_op.drop_constraint('fk_436d3a57-0ae2-5668-937d-8cbc82d15172', type_='foreignkey')
batch_op.drop_column('some_other_table_pk')
现在它导致了一个关键错误,但关键应该已经存在于数据库中?!?
KeyError: 'fk_436d3a57-0ae2-5668-937d-8cbc82d15172'
编辑结束
编辑开始2
好的,所以我在沙箱中玩了一点,似乎它是用户定义的令牌密钥,如果它是 drop_constraint 则无法升级:
以下情况下的 fk_guid 仅在创建约束时调用
convention = {
"fk_guid": some_function,
"ix": "ix_%(column_0_label)s",
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(column_0_name)s",
"fk": "fk_%(fk_guid)s",
# "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s",
}
并给出以下用于创建和删除的迁移代码:
sa.ForeignKeyConstraint(['table_b'], ['table_b.id'], name=op.f('fk_9d68bd30-b17a-565a-aee7-7bd21e90672e')),
# But for dropping, it's not generated:
batch_op.drop_constraint(None, type_='foreignkey')
但奇怪的some_function
是,被调用alembic upgrade head
但与创建调用不同的约束:
def fk_guid(constraint, table):
str_tokens = [
table.name,
] + [
element.parent.name for element in constraint.elements
] + [
element.target_fullname for element in constraint.elements
]
print(f'string_tokens: {str_tokens}')
guid = uuid.uuid5(uuid.NAMESPACE_OID, "_".join(str_tokens))
return str(guid)
def some_function(*params):
print(f'params: {params}')
return fk_guid(*params)
输出:
string_tokens: ['table_a']
将外键的命名约定更改为标准
convention = {
"fk_guid": some_function,
"ix": "ix_%(column_0_label)s",
"uq": "uq_%(table_name)s_%(column_0_name)s",
"ck": "ck_%(table_name)s_%(column_0_name)s",
# "fk": "fk_%(fk_guid)s",
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
"pk": "pk_%(table_name)s",
}
根据需要进行创建和删除:
# creation:
sa.ForeignKeyConstraint(['table_b'], ['table_b.id'], name=op.f('fk_table_a_table_b_table_b')),
# dropping:
batch_op.drop_constraint('fk_table_a_table_b_table_b', type_='foreignkey')
编辑结束2
编辑开始3
github上有一个线程:https ://github.com/sqlalchemy/alembic/issues/713
编辑结束3
解决方案
推荐阅读
- c++ - 如何在 proto3 中使用其名称获取字段对象
- react-native - React Native:“scrollToIndex 应该与 getItemLayout 或 onScrollToIndexFailed 一起使用”
- javascript - 在不同文件中导入相同的 javascript 模块,共享相同的词法范围
- javascript - 如何获取数组中对值的数量?Javascript
- graphql - Magento PWA Studio + 将下拉属性值(产品)检索到前端
- python - 尝试在某些条件下将值从数据帧复制到大小不等的数据帧
- javascript - 我试图制作一个简单有趣的系统,通过输入增加和减少数字,但它不起作用
- javascript - __vue__ 对象在 content_script 上未定义,但出现在浏览器控制台上
- python-3.x - 如何调整参数以实现哪个MV动作更多?
- excel - 如何仅对 column2 进行排序但由空格分隔并删除重复项