我正在尝试对 SQLAlchemy 中机构参与者之间的层次结构和历史关系进行建模(即机构可以有父母/孩子和前任/继任者)。到目前为止,我所掌握的内容很大程度上遵循SQLAlchemy 文档中的有向图示例。现在我希望能够访问字典中节点的左/右邻居作为edge_type键和节点列表作为值,如下所示:node.right_nodes['edge_type']。
我认为这可以通过 collection_class 来完成,但collection_class=attribute_mapped_collection('edge_type')仅使用单个键:值对的结果,而不是键:[值列表]。
实际结果:
>>> node.right_edges['edge_type']
<Edge object>
>>> node.right_nodes['edge_type']
<Node object>
Run Code Online (Sandbox Code Playgroud)
预期结果:
>>> node.right_edges['edge_type']
[<Edge object>, <Edge object>]
>>> node.right_nodes['edge_type']
[<Node object>, <Node object>]
Run Code Online (Sandbox Code Playgroud)
该模型如下所示:
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')
)
)
Run Code Online (Sandbox Code Playgroud)
像这样使用:
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
Run Code Online (Sandbox Code Playgroud)
编辑:
以下内容不是我问题的答案,而是记录我迄今为止所掌握的内容。
association_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}
Run Code Online (Sandbox Code Playgroud)
这当然不会产生所需的列表字典,而是一个字典列表:
>>> node.right_nodes
[{'edge_type': <Node object>}, {'edge_type': <Node object>}]
Run Code Online (Sandbox Code Playgroud)
您还可以简单地在类本身上定义属性,而根本Node不使用它来代理 from to :association_proxyNodeNode
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
Run Code Online (Sandbox Code Playgroud)
这允许所需的视图作为列表的字典:
>>> node.right_nodes
{'edge_type': [<Node object>, <Node object>]}
>>> node.right_nodes['edge_type']
[<Node object>, <Node object>]
Run Code Online (Sandbox Code Playgroud)
但这只是一种方便的看法。
| 归档时间: |
|
| 查看次数: |
1548 次 |
| 最近记录: |