SQLAlchemy ORM中存储字典列表的实现问题及删除报错求助
解决SQLAlchemy ORM字典列表存储的两个问题
咱们来逐个搞定你遇到的这两个问题:
问题1:删除Item时触发AttributeError
原因分析
你自定义的KeyedListCollection把每个key映射成了Prop实例的列表,但SQLAlchemy的relationship集合要求每个元素都是合法的ORM实例(也就是单个Prop对象),而非列表。当执行db.delete(item)时,SQLAlchemy会遍历props集合尝试删除关联对象,结果拿到的是列表,自然就触发了'list' object has no attribute '_sa_instance_state'错误。
解决方案
放弃让集合存储列表,直接让props关系管理所有Prop实例,然后通过props_p属性来实现按key分组的字典列表格式读取。这样SQLAlchemy处理删除时,遍历的都是合法的Prop实例,就不会报错了。
问题2:让props_p支持反向赋值
实现思路
给props_p添加setter方法,遍历传入的字典列表数据:先清理掉当前Item关联的所有旧Prop实例,再根据新数据创建对应的Prop实例并关联到当前Item。如果是小规模数据,直接清空重建的方式简单直观;如果数据量大,也可以做更精细的差异对比更新,不过咱们先从简单版本入手。
修改后的完整代码
import operator from sqlalchemy import Column, ForeignKey, Integer, String, create_engine from sqlalchemy.orm import relationship, sessionmaker from sqlalchemy.ext.declarative import declarative_base connect_args = {} connect_args["check_same_thread"] = False engine = create_engine("sqlite:///test_orm.sqlite", connect_args=connect_args) SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine) db = SessionLocal() Base = declarative_base() class Prop(Base): __tablename__ = "props" id = Column(Integer, primary_key=True, index=True) item_id = Column(Integer, ForeignKey("items.id")) key = Column(String) value = Column(String) item = relationship("Item", back_populates="props") class Item(Base): __tablename__ = "items" id = Column(Integer, primary_key=True, index=True) # 修改:用默认集合类型,直接管理所有Prop实例 props = relationship( "Prop", cascade="all, delete-orphan", back_populates="item", lazy="joined" # 可选:提前加载关联,避免多次查询 ) @property def props_p(self): # 按key分组生成字典列表 out = {} for prop in self.props: if prop.key not in out: out[prop.key] = [] out[prop.key].append(prop.value) return out @props_p.setter def props_p(self, data): # 先清空所有旧的Prop实例 self.props.clear() # 根据新数据创建Prop实例并关联 for key, values in data.items(): for val in values: self.props.append(Prop(key=key, value=val)) Base.metadata.create_all(bind=engine) dat = { "props": { "p1": ["a", "b", "c"], "p2": ["d", "e", "f"], }, } # 测试反向赋值 item = Item() # 直接给props_p赋值字典列表 item.props_p = dat["props"] db.add(item) db.commit() # 读取测试 item = db.query(Item).order_by(Item.id.desc()).first() print(item.props_p) # 输出: {'p1': ['a', 'b', 'c'], 'p2': ['d', 'e', 'f']} # 测试删除 db.delete(item) db.commit() # 验证删除成功 assert db.query(Item).count() == 0 assert db.query(Prop).count() == 0 db.close()
关键修改点说明
- 移除自定义集合类:不再用
KeyedListCollection,让props直接管理所有Prop实例,SQLAlchemy可以正确处理删除逻辑。 - 新增props_p的setter方法:实现了反向赋值逻辑,直接接收字典列表格式的数据,自动转换为Prop实例关联到Item。
- 优化props_p的getter:遍历所有Prop实例按key分组,比原来从集合字典读取更直接。
这样两个问题就都解决了:删除Item时不会再报错,而且可以直接通过item.props_p = {...}来设置数据,非常方便。
内容的提问来源于stack exchange,提问作者Chebyshev
相关产品推荐
相关产品推荐

