SQLAlchemy已加入继承快速批量删除Child对象

Cox*_*oxy 5 python inheritance sqlalchemy

考虑使用联接继承的以下SQLAlchemy映射:

from sqlalchemy import sa

class Location(Base):
    id = Column(Integer, primary_key=True)
    name = sa.Column(sa.String)
    type_ = sa.column(sa.String)

    __tablename__ = 'location'
    __mapper_args__ = {
        'polymorphic_identity': 'location',
        'polymorphic_on': type_,
    }

class Field(Location):
    id = Column(Integer, primary_key=True)
    size = sa.Column(sa.Float)

    __tablename__ = 'field'
    __mapper_args__ = {
        'polymorphic_identity': 'field',
    }
    __table_args__ = (
        sa.ForeignKeyConstraint(['id'], ['location.id']),
    )


session.query(Field).filter(Field.size < 5).delete()
Run Code Online (Sandbox Code Playgroud)

其中base是适当的声明性基础,session是适当的会话对象.上面的实现将导致删除Field对象而不删除父Location对象(因为文档解释清楚,query.delete()不支持继承).我可以通过session.delete(obj)使用ORM删除链中的对象来解决这个问题.但是,这会导致在数据库上执行n个 SQL删除语句(其中n是要删除的对象数).我有一个案例,我可能一次删除100,000个子对象的顺序,所以这个操作非常慢(假设我现在不可能使用带有连接继承的ORM - 我太深了改变这个).

是否有内SQLAlchemy的任何结构或一个合理的选择,这将允许我传递查询类型的对象查询对象Field并适当地删除了项目Location表以及未做ň SQL语句删除?

注意,我目前正在使用PostgreSQL,但是希望保持解决方案db-agnostic.

编辑:根据请求添加表元数据和有关环境的更多信息.

pkt*_*yue 2

经过一两个小时的尝试,我找到了一个代码不多的解决方案。然后我将复制它。

1.我检查了文档delete()。有两句话:

此方法不适用于连接继承映射,因为 SQL 不支持多表删除,并且继承映射器的连接条件不会自动呈现

然而,上述 SQL 不会从 Engineer 表中删除,除非在数据库中建立 ON DELETE CASCADE 规则来处理它。

简而言之,不要将此方法用于连接继承映射,除非您已采取额外的步骤来使其可行。

所以,定义一个外键约束是必要的。像这样:

class Location(Base):
    __tablename__ = 'location'
    id = Column(INTEGER, primary_key=True)
    name = Column(VARCHAR(30))
    type = Column(VARCHAR(30))

    __mapper_args__ = {
        'polymorphic_identity': 'location',
        'polymorphic_on'      : type,
    }

class Field(Location):
    __tablename__ = 'field'
    id = Column(INTEGER, ForeignKey('location.id', ondelete='cascade'), primary_key=True)
    size = Column(DECIMAL(20, 2))

    __mapper_args__ = {
        'polymorphic_identity': 'field',
    }
Run Code Online (Sandbox Code Playgroud)

2.现在,如果我们删除Location, 中的行Field也会被删除。

session.query(Location).filter(Location.id == 1).delete()
Run Code Online (Sandbox Code Playgroud)

3.但是,发帖者不想删除FieldLocation

session.query(Field).filter(Field.size < 5).delete()
Run Code Online (Sandbox Code Playgroud)

这仅删除行 inField而不删除 row in Location。因为Field是外表,所以不能级联主表。

所以,现在我们应该做的是从Location根据中删除Field.size < 5

我努力了

session.query(Location).filter(Field.size < 5).delete()
Run Code Online (Sandbox Code Playgroud)

session.query(Location).outerjoin(Field, Location.id == Field.id).filter(Field.size < 5).delete()
Run Code Online (Sandbox Code Playgroud)

这两个都会抛出异常。

经过多次尝试,我找到的解决方案是这样的:

statment = delete(Field, prefixes=[Location.__tablename__]).where(Field.size == 1)
session.execute(statment)
Run Code Online (Sandbox Code Playgroud)

生成的sql是DELETE location FROM location JOIN field ON location.id = field.id WHERE field.size < 5