SQLAlchemy:多表多态性中的删除

Bar*_*ski 4 python polymorphism orm sqlalchemy

我有这样的模型层次结构:

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',
    }
Run Code Online (Sandbox Code Playgroud)

当我尝试删除所有Foo实例时,db.query(Foo).delete()我得到

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".
Run Code Online (Sandbox Code Playgroud)

嗯,这个错误是有道理的,但是如何让它发挥作用呢?我需要类似cascade关系的东西,但需要多态性。我到处都找不到它。我想到的就是建立一种关系,只是为了维持cascade关系,但这似乎不对。

通常的做法是什么?

zzz*_*eek 6

在这种情况下,有两种删除对象的通用方法。一种是传统的ORM方式:

session.delete(some_bar)
Run Code Online (Sandbox Code Playgroud)

当刷新此会话时,它将发出两个不同的 DELETE 语句,一个用于“bar”表,一个用于“foo”表。然而,ORM per-object delete() 一次仅适用于一个主键。

另一种方式,我认为这就是你想要做的,是这样的:

session.query(Foo).filter(...).delete()
Run Code Online (Sandbox Code Playgroud)

对于这种删除,我们会根据条件发出 DELETE。某些“foo”行可能有一个“bar”行指向它们,而其他行则没有。在关系数据库中,我们可以通过设置ON DELETE CASCADE让数据库为我们处理这个问题。SQLAlchemy 允许您在外键上将其配置为:

ForeignKey('foo.id', ondelete="CASCADE")
Run Code Online (Sandbox Code Playgroud)

上述FK必须发生在建表操作中;例如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
)
Run Code Online (Sandbox Code Playgroud)

然后,当您从“foo”中删除行时,即使在 Postgresql 命令行中,“bar”中的匹配行也会自动删除。SQLAlchemy 不会妨碍这种情况,因此当您使用像 query.delete() 这样的东西时也会发生这种情况。