SQLAlchemy中delete-orphan级联未递归处理待处理对象的问题求助
问题分析与解决方案
这不是SQLAlchemy的bug,是级联规则的作用范围限制导致的:delete-orphan仅处理当前关联层级的对象。当你把Item从Section的items集合移除时,Section的delete-orphan级联会把Item从会话中移除,但此时Item关联的Part并没有被从Item的parts集合中移除——而Item已经被会话丢弃,SQLAlchemy无法追踪到Part现在成了“孤儿”,所以Part会留在会话里,flush时就会以item_id为NULL的状态插入数据库。
无需手动删除对象的解决方法
方法1:ORM+数据库层面联动处理
通过数据库外键的ON DELETE CASCADE配合SQLAlchemy的passive_deletes,让数据库自动清理关联数据,同时让ORM无需追踪无效的Part状态:
- 修改Part的外键定义,添加
ondelete='CASCADE':
class Part(Base): __tablename__ = 'part' id = Column(Integer(), primary_key=True) item_id = Column(ForeignKey('item.id', ondelete='CASCADE'))
- 给Item的parts关联加上
passive_deletes=True:
class Item(Base): __tablename__ = 'item' id = Column(Integer(), primary_key=True) section_id = Column(ForeignKey('section.id')) parts = relationship( Part, cascade='save-update, merge, delete, delete-orphan', passive_deletes=True )
这样当Item被移除(或删除)时,数据库会自动删掉对应的Part,ORM也不会把无效的Part留在会话里。
方法2:先清空Item的parts集合再移除Item
在把Item从Section的items里移除前,先清空Item的parts集合,触发Item与Part之间的delete-orphan级联,把Part从会话中移除:
section = session.get(Section, 1) part = Part() item = Item(parts=[part]) section.items.append(item) # 先清空item的parts,触发级联移除part item.parts.clear() # 再移除item section.items.remove(item) item in session # False,符合预期 part in session # False,符合预期 session.flush() # 不会插入任何无效数据
方法3:把Item的parts关联级联改为all, delete-orphan
all包含了save-update, merge, delete这些规则,能确保当Item被delete-orphan级联移除时,关联的Part也会被级联处理:
class Item(Base): __tablename__ = 'item' id = Column(Integer(), primary_key=True) section_id = Column(ForeignKey('section.id')) parts = relationship( Part, cascade='all, delete-orphan' )
这个方法不需要额外操作,只要Item被标记为需要移除,关联的Part就会跟着被处理。
内容的提问来源于stack exchange,提问作者Ikor Jefocur
相关产品推荐
相关产品推荐

