[英]SQLAlchemy ORM Collection Dict of Lists
我正在尝试创建一个 ORM model 以将列表字典存储在 SQLAlchemy 中。 基于https://gist.github.com/onecrayon/646da61accf54674d4f5098376a2c5df ,我已经取得了一些进展,但我坚持使用下面的代码:
import operator
from sqlalchemy import Column, ForeignKey, Integer, String, create_engine
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm.collections import MappedCollection, collection, _instrument_class
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 KeyedListCollection(MappedCollection):
def __init__(self, key):
super().__init__(operator.attrgetter(key))
@collection.internally_instrumented
def __setitem__(self, key, value, _sa_initiator=None):
if not super().get(key):
super().__setitem__(key, [], _sa_initiator)
super().__getitem__(key).append(value)
_instrument_class(KeyedListCollection)
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)
props = relationship(
"Prop",
collection_class=lambda: KeyedListCollection("key"),
cascade="all, delete-orphan",
back_populates="item",
)
@property
def props_p(self):
out = {}
for k, vs in self.props.items():
out[k] = [v.value for v in vs]
return out
Base.metadata.create_all(bind=engine)
dat = {
"props": {
"p1": [
"a",
"b",
"c",
],
"p2": [
"d",
"e",
"f",
],
},
}
item = Item()
db.add(item)
db.commit()
db.refresh(item)
props = []
for k, vs in dat["props"].items():
props.extend([Prop(key=k, value=v, item=item) for v in vs])
[db.add(p) for p in props]
db.commit()
item = db.query(Item).order_by(Item.id.desc()).first()
print(item.props_p)
db.delete(item)
db.commit()
db.close()
db.delete(item)
行引发AttributeError: 'list' object has no attribute '_sa_instance_state'
。 我假设我必须向我的自定义映射器添加某种删除方法,但我不知道如何。 我尝试覆盖__delitem__
,但这似乎甚至没有被调用。
来自print(item.props_p)
的 output 是我正在寻找的,但我不认为在 Z20F35E630DAF44DBFA4C3F68F5399D8C 中使用 function 是正确的,因为它是正确的使用方式具有该模式的数据(如示例中的字典dat
)并正确存储。
我知道我应该以某种方式使用association_proxy
,因为我已经将它与attribute_mapped_collection
结合起来制作字符串字典,但我不知道如何使它适用于列表字典。
有人对我有想法吗?
经过更多的摆弄,我将Item
更改为:
class Item(Base):
__tablename__ = "items"
id = Column(Integer, primary_key=True, index=True)
_props = relationship(
"Prop",
cascade="all, delete-orphan",
back_populates="item",
)
@property
def props(self):
out = {}
for p in self._props:
if p.key not in out:
out[p.key] = []
out[p.key].append(p.value)
return out
@props.setter
def props(self, props):
ps = []
for k, vs in props.items():
ps.extend([Prop(key=k, value=v) for v in vs])
self._props = ps
这似乎正是我想要的。 我不确定这是“正确的方法”,但它可以完成工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.