SQLAlchemy:在多表多态中删除

4 投票
1 回答
1197 浏览
提问于 2025-04-18 07:04

我有一个这样的模型层级:

class Foo(Base):

    id = Column(
        Integer, 
        Sequence('foo_id_seq', start=1001, increment=1),
        primary_key=True
    )
    discriminator = Column('type', String(20), nullable=False)


    __tablename__ = 'foo'
    __mapper_args__ = {
        'polymorphic_on': discriminator,
        'polymorphic_identity': 'foo',
    }

class Bar(Foo):
    id = Column(Integer, ForeignKey('foo.id'), primary_key=True)

    __tablename__ = 'bar'
    __mapper_args__ = {
        'polymorphic_identity': 'bar',
    }

当我尝试用 db.query(Foo).delete() 删除所有的 Foo 实例时,我遇到了这个错误:

IntegrityError: (IntegrityError) ERROR:  update or delete on table "foo" violates foreign key constraint "bar_foo_id_fkey" on table "bar"
DETAIL:  Key (id)=(68575) is still referenced from table "bar".

这个错误是可以理解的,但我该怎么解决呢?我需要一种类似于关系中的 cascade 的功能,但这是针对多态的。我在网上找不到相关的信息。我想到的办法是创建一个关系,然后在里面使用 cascade,但这样似乎不太对。

通常应该怎么做呢?

1 个回答

6

在这种情况下,删除对象主要有两种方法。第一种是传统的ORM(对象关系映射)方式:

session.delete(some_bar)

当这个会话被刷新时,它会发出两个不同的删除语句,一个针对“bar”表,另一个针对“foo”表。不过,ORM的每个对象的delete()方法一次只能处理一个主键。

第二种方法,我觉得这就是你想要做的,类似于这样:

session.query(Foo).filter(...).delete()

使用这种删除方式,我们是在根据某些条件进行删除。有些“foo”表的行可能有指向它们的“bar”行,而有些则没有。在关系型数据库中,我们可以通过设置ON DELETE CASCADE来让数据库自动处理这些情况。SQLAlchemy允许你在外键上进行这样的配置:

ForeignKey('foo.id', ondelete="CASCADE")

上面的外键设置必须在创建表时进行,比如在metadata.create_all()时,你会看到类似的输出:

CREATE TABLE bar (
    id SERIAL NOT NULL, 
    foo_id INTEGER, 
    PRIMARY KEY (id), 
    FOREIGN KEY(foo_id) REFERENCES foo (id) ON DELETE CASCADE
)

然后,当你从“foo”表中删除行时,即使是在Postgresql命令行中,匹配的“bar”表中的行也会被自动删除。SQLAlchemy不会干扰这个过程,所以当你使用像query.delete()这样的操作时,这也会发生。

撰写回答