Python 如何在SQLAlchemy中使用collection_类?

Python 如何在SQLAlchemy中使用collection_类?,python,sqlalchemy,Python,Sqlalchemy,我试图在SQLAlchemy中模拟机构参与者之间的等级和历史关系(即,机构可以有父母/子女和前任/继任者)。到目前为止,我主要遵循SQLAlchemy文档中的内容。现在,我希望能够访问字典中节点的左/右邻居,将edge\u type作为键,将节点列表作为值,如下所示:node.right\u nodes['edge\u type'] 我认为这可以通过collection\u类实现,但是使用collection\u class=attribute\u mapped\u collection('ed

我试图在SQLAlchemy中模拟机构参与者之间的等级和历史关系(即,机构可以有父母/子女和前任/继任者)。到目前为止,我主要遵循SQLAlchemy文档中的内容。现在,我希望能够访问字典中节点的左/右邻居,将
edge\u type
作为键,将节点列表作为值,如下所示:
node.right\u nodes['edge\u type']

我认为这可以通过collection\u类实现,但是使用
collection\u class=attribute\u mapped\u collection('edge\u type')
只会产生一个键:值对,而不是键:[值列表]

实际结果:

>>> node.right_edges['edge_type']
<Edge object>
>>> node.right_nodes['edge_type']
<Node object>
这样使用:

from sqlalchemy import (Column, Integer, String, ForeignKey,
                        create_engine)
from sqlalchemy.orm import Session, relationship, backref
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm.collections import attribute_mapped_collection


Base = declarative_base()


class Node(Base):
    __tablename__ = 'node'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)

    left_nodes = association_proxy('left_edges', 'left_node')
    right_nodes = association_proxy('right_edges', 'right_node')


class Edge(Base):
    __tablename__ = 'edge'

    left_node_id = Column(Integer, ForeignKey('node.id'), primary_key=True)
    right_node_id = Column(Integer, ForeignKey('node.id'), primary_key=True)
    edge_type = Column(String)

    left_node = relationship(
        Node,
        foreign_keys=left_node_id,
        backref=backref(
            'right_edges',
            collection_class=attribute_mapped_collection('edge_type')
        )
    )

    right_node = relationship(
        Node,
        foreign_keys=right_node_id,
        backref=backref(
            'left_edges',
            collection_class=attribute_mapped_collection('edge_type')
        )
    )
engine = create_engine('sqlite://', echo=True)
Base.metadata.create_all(engine)
session = Session(engine)

n1 = Node(name='LeftNode')
n2 = Node(name='RightNode1')
n3 = Node(name='RightNode2')
Edge(left_node=n1, right_node=n2, edge_type='hierarchy')
Edge(left_node=n1, right_node=n3, edge_type='hierarchy')
session.add_all([n1, n2, n3])
session.commit()

print(n1.right_nodes)  # returns dict with 1 node as value
print(n1.right_nodes['hierarchy'])  # returns 1 node
print(n1.right_edges)  # returns dict with 1 edge as value
print(n1.right_edges['hierarchy'])  # returns 1 edge
print(session.query(Edge).filter_by(left_node=n1).all())  # returns list of 2 edges
编辑:

以下不是对我的问题的回答,而是记录我迄今为止所做的工作

association\u proxy
不仅将字段作为目标,还可以在目标类中定义属性:

class Node(Base):

    # <snip>

    left_nodes = association_proxy('left_edges', 'left_nodes_edge_type')
    right_nodes = association_proxy('right_edges', 'right_nodes_edge_type')


class Edge(Base):

    # <snip>

    @property
    def left_nodes_edge_type(self):
        return {self.edge_type: self.left_node}

    @property
    def right_nodes_edge_type(self):
        return {self.edge_type: self.left_node}
这允许将所需的视图作为列表的目录:

>>> node.right_nodes
{'edge_type': [<Node object>, <Node object>]}
>>> node.right_nodes['edge_type']
[<Node object>, <Node object>]
>>node.right\u节点
{'edge_type':[,]}
>>>node.right_nodes['edge_type']
[, ]
但这只是一个方便的观点

>>> node.right_nodes
[{'edge_type': <Node object>}, {'edge_type': <Node object>}]
class Node(Base):

    # <snip>

    @property
    def left_nodes(self):
        d = defaultdict(list)
        for edge in self.left_edges:
            d[edge.edge_type].append(edge.left_node)
        return d

    @property
    def right_nodes(self):
        d = defaultdict(list)
        for edge in self.right_edges:
            d[edge.edge_type].append(edge.right_node)
        return d
>>> node.right_nodes
{'edge_type': [<Node object>, <Node object>]}
>>> node.right_nodes['edge_type']
[<Node object>, <Node object>]