在使用SQLAlchemy时,我们有时需要将属性映射到列表。例如,我们有一个Parent表和一个Child表,Parent表中存储父节点的信息,Child表中存储子节点的信息。我们希望在Parent对象中有一个children属性,该属性的值是一个列表,其中包含所有属于该父节点的子节点。
- 解决方案
为了实现这一目标,我们需要使用一个中介表
ParentChild。ParentChild表中存储父节点和子节点的关联关系,以及子节点的序号。然后,我们可以使用SQLAlchemy的relation()函数来建立Parent和ParentChild表之间的关系,并使用collection_class参数来指定我们自己的集合类。
这里是一个示例代码:
from sqlalchemy import *
from sqlalchemy.orm import mapper, relation, sessionmaker
from sqlalchemy.orm.collections import collection
metadata = MetaData()
parent = Table('parent', metadata,
Column('parent_id', Integer, primary_key=True),
Column('name', Unicode),
)
child = Table('child', metadata,
Column('child_id', Integer, primary_key=True),
Column('name', Unicode),
)
parent_child = Table('parent_child', metadata,
Column('parent_id', Integer, ForeignKey(parent.c.parent_id)),
Column('child_id', Integer, ForeignKey(child.c.child_id)),
Column('number', Integer),
PrimaryKeyConstraint('parent_id', 'child_id'),
)
class ParentChild(object):
def __init__(self, child, number):
self.child = child
self.number = number
class Parent(object): pass
class Child(object): pass
class MyMappedCollection(object):
def __init__(self, data=None):
self._data = data or {}
@collection.appender
def _append(self, parent_child):
l = self._data.setdefault(parent_child.number, [])
l.append(parent_child)
def __setitem__(self, number, child):
self._append(ParentChild(number=number, child=child))
def __getitem__(self, number):
return tuple(pc.child for pc in self._data[number])
@collection.remover
def _remove(self, parent_child):
self._data[parent_child.number].remove(parent_child)
@collection.iterator
def _iterator(self):
for pcs in self._data.itervalues():
for pc in pcs:
yield pc
def __repr__(self):
return '%s(%r)' % (type(self).__name__, self._data)
mapper(Parent, parent, properties={
'children': relation(ParentChild, collection_class=MyMappedCollection),
})
mapper(Child, child)
mapper(ParentChild, parent_child, properties={
'parent': relation(Parent),
'child': relation(Child),
})
engine = create_engine('sqlite://')
db = sessionmaker(bind=engine)()
metadata.create_all(bind=engine)
p = Parent()
http://www.jshk.com.cn/mb/reg.asp?kefu=xiaoding;//爬虫IP免费获取;
c1 = Child()
c2 = Child()
c3 = Child()
p.children[1] = c1
p.children[1] = c2
p.children[2] = c3
db.add(p)
db.commit()
p_id = p.parent_id
db.expunge_all()
p = db.query(Parent).get(p_id)
print p.children[1]
print p.children[2]
输出结果:
(Child(), Child())
(Child(),)
从输出结果中,我们可以看到,Parent对象的children属性是一个列表,其中包含所有属于该父节点的子节点。