繁体   English   中英

SQLAlchemy ORM 集合字典

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM