具有自引用外键的 Alembic SQLite ALTER TABLE
Alembic SQLite ALTER TABLE with self-referencing foreign key
SQLite 数据库的 Alembic 迁移:
def upgrade():
with op.batch_alter_table('my_table') as batch_op:
batch_op.add_column(sa.Column('parent_id', sa.String(24)))
batch_op.create_foreign_key('parent_constraint', 'my_table', ['parent_id'], ['id'])
应该创建外键 parent_id
引用 id
相同 table my_table
,创建对 table 的引用 table =16=]:
CREATE TABLE "my_table" (
id VARCHAR(24) NOT NULL,
parent_id VARCHAR(24),
PRIMARY KEY (id),
CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES _alembic_batch_temp (id)
)
如何在更改 table 时创建自引用约束?
经过一些研究,我发现这里的问题是 Alembic 进行批量迁移的方式。简而言之,在当前版本 (0.7.6) 的 Alembic 中,无法通过迁移与自身建立关系。
如 Alembic documentation 中所述,要进行迁移,将使用临时名称创建新的 table 并从 alter table 进行更改
代码。在这种情况下:
CREATE TABLE _alembic_batch_temp (
id VARCHAR(24) NOT NULL,
parent_id VARCHAR(24),
PRIMARY KEY (id),
CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES _alembic_batch_temp (id)
)
table填充了旧table的数据:
INSERT INTO _alembic_batch_temp (id) SELECT id FROM my_table;
然后删除旧的table:
DROP TABLE my_table;
最后,新创建的 table 被重命名为它的正确名称:
ALTER TABLE _alembic_batch_temp RENAME TO my_table;
这种处理方式的问题已经在第一个代码片段中显现出来。新创建的外键引用临时 table 并且一旦创建,由于 SQLite 中的限制就不能更改。因此,在重命名 table 之后,您最终会得到您提供的 table:
CREATE TABLE "my_table" ( # new name
id VARCHAR(24) NOT NULL,
parent_id VARCHAR(24),
PRIMARY KEY (id),
CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES _alembic_batch_temp (id) # old reference
)
为避免这种情况,您可以手动创建批量迁移:
将旧的 table 重命名为某个临时名称:
ALTER TABLE my_table RENAME TO migration_temp_table;
使用正确的名称和正确的引用创建新的 table:
CREATE TABLE my_table (
id VARCHAR(24) NOT NULL,
parent_id VARCHAR(24),
PRIMARY KEY (id),
CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES my_table (id)
)
复制数据:
INSERT INTO my_table (id) SELECT id FROM migration_temp_table;
去掉旧的table:
DROP TABLE migration_temp_table;
SQLite 数据库的 Alembic 迁移:
def upgrade():
with op.batch_alter_table('my_table') as batch_op:
batch_op.add_column(sa.Column('parent_id', sa.String(24)))
batch_op.create_foreign_key('parent_constraint', 'my_table', ['parent_id'], ['id'])
应该创建外键 parent_id
引用 id
相同 table my_table
,创建对 table 的引用 table =16=]:
CREATE TABLE "my_table" (
id VARCHAR(24) NOT NULL,
parent_id VARCHAR(24),
PRIMARY KEY (id),
CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES _alembic_batch_temp (id)
)
如何在更改 table 时创建自引用约束?
经过一些研究,我发现这里的问题是 Alembic 进行批量迁移的方式。简而言之,在当前版本 (0.7.6) 的 Alembic 中,无法通过迁移与自身建立关系。
如 Alembic documentation 中所述,要进行迁移,将使用临时名称创建新的 table 并从 alter table 进行更改 代码。在这种情况下:
CREATE TABLE _alembic_batch_temp ( id VARCHAR(24) NOT NULL, parent_id VARCHAR(24), PRIMARY KEY (id), CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES _alembic_batch_temp (id) )
table填充了旧table的数据:
INSERT INTO _alembic_batch_temp (id) SELECT id FROM my_table;
然后删除旧的table:
DROP TABLE my_table;
最后,新创建的 table 被重命名为它的正确名称:
ALTER TABLE _alembic_batch_temp RENAME TO my_table;
这种处理方式的问题已经在第一个代码片段中显现出来。新创建的外键引用临时 table 并且一旦创建,由于 SQLite 中的限制就不能更改。因此,在重命名 table 之后,您最终会得到您提供的 table:
CREATE TABLE "my_table" ( # new name
id VARCHAR(24) NOT NULL,
parent_id VARCHAR(24),
PRIMARY KEY (id),
CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES _alembic_batch_temp (id) # old reference
)
为避免这种情况,您可以手动创建批量迁移:
将旧的 table 重命名为某个临时名称:
ALTER TABLE my_table RENAME TO migration_temp_table;
使用正确的名称和正确的引用创建新的 table:
CREATE TABLE my_table ( id VARCHAR(24) NOT NULL, parent_id VARCHAR(24), PRIMARY KEY (id), CONSTRAINT parent_constraint FOREIGN KEY(parent_id) REFERENCES my_table (id) )
复制数据:
INSERT INTO my_table (id) SELECT id FROM migration_temp_table;
去掉旧的table:
DROP TABLE migration_temp_table;