一尘不染

SQLAlchemy:级联删除

python

我必须缺少SQLAlchemy的层叠选项的琐碎内容,因为我无法获得简单的层叠删除来正确操作-如果删除了父元素,则子对象将使用null外键保留。

我在这里放了一个简洁的测试用例:

from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Parent(Base):
    __tablename__ = "parent"
    id = Column(Integer, primary_key = True)

class Child(Base):
    __tablename__ = "child"
    id = Column(Integer, primary_key = True)
    parentid = Column(Integer, ForeignKey(Parent.id))
    parent = relationship(Parent, cascade = "all,delete", backref = "children")

engine = create_engine("sqlite:///:memory:")
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)

session = Session()

parent = Parent()
parent.children.append(Child())
parent.children.append(Child())
parent.children.append(Child())

session.add(parent)
session.commit()

print "Before delete, children = {0}".format(session.query(Child).count())
print "Before delete, parent = {0}".format(session.query(Parent).count())

session.delete(parent)
session.commit()

print "After delete, children = {0}".format(session.query(Child).count())
print "After delete parent = {0}".format(session.query(Parent).count())

session.close()

输出:

Before delete, children = 3
Before delete, parent = 1
After delete, children = 3
After delete parent = 0

父母与子女之间存在简单的一对多关系。该脚本创建一个父级,添加3个子级,然后提交。接下来,它删除父级,但子级仍然存在。为什么?如何使孩子级联删除?


阅读 210

收藏
2020-12-20

共1个答案

一尘不染

问题是sqlalchemy认为Child是父级的,因为这是您定义关系的地方(当然,不在乎您将其称为“子级”)。

如果在Parent类上定义关系,它将起作用:

children = relationship("Child", cascade="all,delete", backref="parent")

(请注意"Child"为字符串:使用声明式样式时允许使用,这样您就可以引用尚未定义的类)

您可能还想添加delete-orphandelete导致删除父级时删除子级,delete- orphan也删除从父级“删除”的所有子级,即使未删除父级也是如此)

编辑:刚刚发现:如果您 确实 想在Child类上定义关系,则可以这样做,但是您将必须 在backref上
定义级联(通过显式创建backref),如下所示:

parent = relationship(Parent, backref=backref("children", cascade="all,delete"))

(暗示from sqlalchemy.orm import backref

2020-12-20