我有一个看起来像这样的表:
values = Table('values',
...
Column('person_id', Unicode(34), ForeignKey('persons.guid')),
Column('type', Text, nullable=False),
Column('name', Text, nullable=False),
Column('params', HSTORE, default={}),
UniqueConstraint('person_id', 'type', 'name'),
我想在relationship
对象上配置Person
,以便我可以
p = Person()
p.values['a type']['a name'] = {'my': 'params'}
让它按预期工作。
我查看了http://docs.sqlalchemy.org/en/latest/_modules/examples/association/dict_of_sets_with_default.html,它看起来与我想要的类似,包括类似于defaultdict
的好功能。看起来它通过关联表,并提供多对多关系。我只想要一对多的关系,并希望每个Person
在许多name
值的每一个下都有许多type
个值。
答案 0 :(得分:4)
在一个表中执行此操作意味着您只需在现有集合上编写。尝试实现此目的有几种方法,但所有这些方法都需要编写非常精细的嵌套集合类。一种方法是编写此嵌套集合类,以便将所有更改保留为基于列表的简单关系。这是一个简单的方法,但它不是非常有效,因为您必须在更改时重写支持列表或以其他方式搜索它。另一种方法是尝试与集合装饰器探戈。部分内容如下。请注意,我们完全是自定义的,所以我们只是跳过关联代理......必须考虑每个 setitem / getitem 。下面我只使用一个字符串作为值,可以用hstore替换。
from sqlalchemy.orm.collections import MappedCollection, \
collection_adapter, collection
class MySubMagicDict(dict):
def __init__(self, parent, key):
self.parent = parent
self.key = key
def __setitem__(self, key, value):
self.parent.__setitem__(self.key,
Value(type=self.key, name=key, value=value))
class MyMagicDict(MappedCollection):
def __missing__(self, key):
d = MySubMagicDict(self, key)
dict.__setitem__(self, key, d)
return d
def __getitem__(self, key):
d = MySubMagicDict(self, key)
d.update(
(v.name, v.value) for v in dict.__getitem__(self, key).values()
)
return d
@collection.internally_instrumented
def __setitem__(self, key, value, _sa_initiator=None):
if _sa_initiator is not False:
executor = collection_adapter(self)
if executor:
value = executor.fire_append_event(value, _sa_initiator)
dict.__setitem__(
dict.__getitem__(self, key), value.name, value)
@collection.converter
def _convert(self, dictlike):
for incoming_key, value in dictlike.items():
for name, subvalue in value.items():
yield Value(type=incoming_key, name=name, value=subvalue)
@collection.iterator
def _all_items(self):
for value in self.values():
for subvalue in value.values():
yield subvalue
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
values = Table('values', Base.metadata,
Column("id", Integer, primary_key=True),
Column('person_id', Integer, ForeignKey('persons.id')),
Column('type', Text, nullable=False),
Column('name', Text, nullable=False),
Column('value', String)
)
class Value(Base):
__table__ = values
class Person(Base):
__tablename__ = 'persons'
id = Column(Integer, primary_key=True)
values = relationship("Value", collection_class=
lambda: MyMagicDict(lambda item: item.type),
cascade="all, delete-orphan"
)
e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
s = Session(e)
p = Person(values={
't1': {
'n1': 't1n1',
'n2': 't1n2'
},
't2': {
'n1': 't2n1',
'n2': 't2n2'
}
})
s.add(p)
s.commit()
assert p.values['t2']['n2'] == 't2n2'
assert p.values['t1']['n1'] == 't1n1'
p.values['t1']['n2'] = 't1n2mod'
s.commit()
assert p.values['t1']['n2'] == 't1n2mod'
assert p.values['t1']['n1'] == 't1n1'