如何在SQLAlchemy中删除外键约束?

13

我正在使用SQLAlchemy Migrate来跟踪数据库更改,但在删除外键时遇到了问题。我有两个表,t_new是新表,t_exists是现有表。我需要添加t_new,然后添加一个外键到t_exists。然后我需要能够撤消该操作(这就是我遇到问题的地方)。

t_new = sa.Table("new", meta.metadata,
    sa.Column("new_id", sa.types.Integer, primary_key=True)
)
t_exists = sa.Table("exists", meta.metadata,
    sa.Column("exists_id", sa.types.Integer, primary_key=True),
    sa.Column(
        "new_id", 
        sa.types.Integer,
        sa.ForeignKey("new.new_id", onupdate="CASCADE", ondelete="CASCADE"),
        nullable=False
    )
)

这个运行良好:

t_new.create()
t_exists.c.new_id.create()

但是这个不行:

t_exists.c.new_id.drop()
t_new.drop()

尝试删除外键列时出现错误:1025,“Error on rename of '.\my_db_name\#sql-1b0_2e6' to '.\my_db_name\exists' (errno: 150)”

如果我使用原始SQL执行此操作,则可以手动删除外键,然后删除该列,但我无法找出如何使用SQLAlchemy删除外键? 如何删除外键,然后再删除该列呢?

4个回答

8
你可以使用sqlalchemy.migrate来完成。
为了使其工作,我必须显式地创建外键约束,而不是通过Column('fk', ForeignKey('fk_table.field'))隐式创建:
可惜的是,不是这样做:
p2 = Table('tablename',
            metadata,
            Column('id', Integer, primary_key=True),
            Column('fk', ForeignKey('fk_table.field')),
            mysql_engine='InnoDB',
           )

请这样做:

p2 = Table('tablename',
            metadata,
            Column('id', Integer, primary_key=True),
            Column('fk', Integer, index=True),
            mysql_engine='InnoDB',
            )
ForeignKeyConstraint(columns=[p2.c.fk], refcolumns=[p3.c.id]).create()

然后删除过程如下所示:
def downgrade(migrate_engine):
     # First drop the constraint
     ForeignKeyConstraint(columns=[p2.c.fk], refcolumns=[p3.c.id]).drop()
     # Then drop the table
     p2.drop()

5
如果有人遇到这个问题 - ForeignKeyConstraint 需要从 migrate 而不是 sqlalchemy 中导入。 - mjallday
1
我不知道在哪个版本的SQLAlchemy中删除了drop方法,但是我成功地使用op.drop_constraint(_FK_NAME, _TABLE_NAME, type_="foreignkey")实现了相同的效果,并通过以下方式创建:op.create_foreign_key(_FK_NAME, _TABLE_NAME, "dest_table", ["src_column"], ["dest_column"]) - giuliano-oliveira

3

我通过创建一个单独的元数据实例并使用Session.execute()运行原始SQL来完成这个操作。理想情况下,应该有一种只使用sqlalchemy的解决方案,这样我就不必使用特定于MySQL的解决方案。但是目前,我不知道有这样的解决方案。


6
你好,你执行的 SQL 语句是什么? - a paid nerd

0

我相信你可以通过SQLAlchemy-Migrate实现这一点。请注意,ForeignKey位于独立的列上。ForeignKeyConstraint位于表级别,并将列关联在一起。如果您查看列上的ForeignKey对象,您将看到它引用了一个ForeignKeyConstraint。

由于我使用的两个数据库中,MS SQL不受SqlAlchemy-Migrate支持,而sqlite不支持约束的“alter table”,因此我无法测试这个想法。但我确实让SQLAlchemy尝试通过在sqlite表上删除引用约束来删除FK,所以看起来很不错。但是,具体情况可能有所不同。


-1

嗯,在SQLAlchemy中,您可以实现这一点:在drop()列之前,只需drop()所有约束(理论上,您可能有多个约束):

def drop_column(column):
    for fk in column.table.foreign_keys:
        if fk.column == column:
            print 'deleting fk ', fk
            fk.drop()
    column.drop()

drop_column(t_exists.c.new_id)

我试过这个,但出现了一个错误:'ForeignKey'对象没有属性 'drop'。我查看了文档,但没有找到任何方法可以实现这一点:http://www.sqlalchemy.org/docs/reference/sqlalchemy/schema.html?highlight=foreignkey#sqlalchemy.schema.ForeignKey - Travis
这是公平的,但请向我展示具有drop()函数的Column在SA文档中的文档:http://www.sqlalchemy.org/docs/reference/sqlalchemy/schema.html?highlight=foreignkey#sqlalchemy.schema.Column - van
"SQLAlchemy-Migrate" 添加了 drop()、create() 和其他便利功能,但需要单独安装。请参见 http://packages.python.org/sqlalchemy-migrate/index.html。 - OrganicPanda
SQLAlchemy-Migrate 为 ForeignKeyConstraint 添加了 drop() 和 create() 方法,而不是 ForeignKey。 - Ben

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接