在这种情况下,我需要更改外键约束以实现级联删除,并为更改编写迁移步骤。我使用带有peewee的烧瓶作为ORM工具,使用peewee_migrate作为迁移工具。
当从peewee_migrate路由器中提供的迁移模板创建迁移时,将向迁移添加一组可用的方法。
"""Peewee migrations -- 007_Added_cascade_delete.py.
Some examples (model - class or model name)::
> Model = migrator.orm['model_name'] # Return model in current state by name
> migrator.sql(sql) # Run custom SQL
> migrator.python(func, *args, **kwargs) # Run python code
> migrator.create_model(Model) # Create a model (could be used as decorator)
> migrator.remove_model(model, cascade=True) # Remove a model
> migrator.add_fields(model, **fields) # Add fields to a model
> migrator.change_fields(model, **fields) # Change fields
> migrator.remove_fields(model, *field_names, cascade=True)
> migrator.rename_field(model, old_field_name, new_field_name)
> migrator.rename_table(model, new_table_name)
> migrator.add_index(model, *col_names, unique=False)
> migrator.drop_index(model, *col_names)
> migrator.add_not_null(model, *field_names)
> migrator.drop_not_null(model, *field_names)
> migrator.add_default(model, field_name, default)
"""
我尝试使用remove_fields方法删除列,然后添加一个新的外键列,其中使用add_fields方法进行级联删除,但没有成功,因为该列没有被删除,但没有显示错误,这会导致具有该名称的外键已经存在的错误。
在使用change_fields方法时,我得到一个错误,即drop_foreign_key_constraint没有在peewee的游戏室迁移中实现,因为change_fields方法试图在更改字段之前删除外键,如果更改的字段也是外键,则尝试使用add_foreign_key_constraint方法添加外键约束(也未实现)
@operation
def drop_foreign_key_constraint(self, table, column_name):
raise NotImplementedError
@operation
def add_foreign_key_constraint(self, table, column_name, field):
raise NotImplementedError
我想出的唯一解决方案是使用允许执行自定义sql的sql方法,但问题是这只能在特定的数据库上工作,因为postgresql、mysql和sqlite之间的语法不同。用于开发/生产的数据库是postgres,而sqlite内存中的数据库用于测试,这会导致所有测试失败,但是迁移在开发/生产上成功,因此这是一个“解决方案”。
if current_app.config["TESTING"] != True:
def migrate(migrator, database, fake=False, **kwargs):
"""Write your migrations here."""
migrator.sql('alter table {} drop constraint {};'.format(TABLE_NAME, CONSTRAINT_NAME))
migrator.sql('alter table {} add constraint {} foreign key (device_id) references device(device_id) on delete cascade;'.format(TABLE_NAME, CONSTRAINT_NAME))
def rollback(migrator, database, fake=False, **kwargs):
"""Write your rollback migrations here."""
migrator.sql('alter table {} drop constraint {};'.format(TABLE_NAME, CONSTRAINT_NAME))
migrator.sql('alter table {} add constraint {} foreign key (device_id) references device(device_id);'.format(TABLE_NAME, CONSTRAINT_NAME))
else:
def migrate(migrator, database, fake=False, **kwargs):
"""Write your migrations here."""
def rollback(migrator, database, fake=False, **kwargs):
"""Write your rollback migrations here."""
由于sqlite不支持alter table drop constraint
,我理解您需要创建一个表的副本,将原始表中的所有数据插入一个副本中,然后删除原始表并重命名复制表,但我认为这是过头了。
你能给我一个更好的方法来完成这件事吗?我可以在不使用自定义peewee_migrate的情况下以某种方式使用它,这样peewee就可以与数据库无关了吗?
发布于 2018-06-28 06:29:54
由于sqlite不支持alter约束,我理解您需要创建一个表的副本,将原始表中的所有数据插入到一个副本中,然后删除原始表并重命名复制表,但我认为这是过头了。
我对"peewee_migrate“一无所知,但是peewee本身附带了一个迁移扩展。Peewee公开了一个与数据库无关的API,用于执行通常的操作(添加/删除/重命名列、约束等),但是由于sqlite支持的操作类型的限制,sqlite迁移实现必须做一些变通。
例如,sqlite可以添加列,但不能删除它们。因此,peewee在删除列时所做的工作是重命名表,在没有列的情况下重新创建表,复制数据,并删除旧的副本。
不幸的是,sqlite不支持在创建表之后添加约束,因此对于您的情况,最好的选择是可能删除列,然后重新添加它。
我编写了一个测试用例,展示了它是如何工作的,并验证了它是否正确工作:
https://stackoverflow.com/questions/51076170
复制相似问题