我在网络应用中使用SQLAlchemy作为我的ORM。当我尝试创建一个新对象并将其添加为其他对象的子对象时,我得到以下异常:
Traceback (most recent call last):
File "/usr/local/lib/python3.4/dist-packages/tornado/web.py", line 1346, in _execute
result = method(*self.path_args, **self.path_kwargs)
File "/usr/share/app/server/handlers.py", line 248, in wrapper
return fn(self, *args, **kwargs)
File "/usr/share/app/server/crud/node.py", line 157, in post
session.flush()
File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/session.py", line 2004, in flush
self._flush(objects)
File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/session.py", line 2122, in _flush
transaction.rollback(_capture_exception=True)
File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/util/langhelpers.py", line 60, in __exit__
compat.reraise(exc_type, exc_value, exc_tb)
File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/util/compat.py", line 182, in reraise
raise value
File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/session.py", line 2086, in _flush
flush_context.execute()
File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/unitofwork.py", line 373, in execute
rec.execute(self)
File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/unitofwork.py", line 532, in execute
uow
File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/persistence.py", line 149, in save_obj
base_mapper, states, uowtransaction
File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/persistence.py", line 270, in _organize_states_for_save
states):
File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/persistence.py", line 1035, in _connections_for_states
for state in _sort_states(states):
File "/usr/local/lib/python3.4/dist-packages/sqlalchemy/orm/persistence.py", line 1057, in _sort_states
sorted(persistent, key=lambda q: q.key[1])
TypeError: unorderable types: str() < UUID()
主键由两个UUID组成(如多租户)。我正在使用sqlalchemy.ext.orderinglist
对子项进行排序,并使用整数seq
列来存储索引。创建新对象的代码大致是:
node = Node(group_id=group_id)
hierarchy = (session.query(Hierarchy).get((hierarchy_id, group_id)))
node.hierarchy = hierarchy
hierarchy.nodes.append(node)
hierarchy.nodes.reorder()
session.flush()
为了完整性,以下是相关的映射和关系:
class Group(Base):
__tablename__ = 'group'
id = Column(GUID, default=uuid.uuid4, primary_key=True)
class Hierarchy(Base):
__tablename__ = 'hierarchy'
id = Column(GUID, default=uuid.uuid4, primary_key=True)
group_id = Column(
GUID, ForeignKey('group.id'), nullable=False, primary_key=True)
class Node(Base):
__tablename__ = 'node'
id = Column(GUID, default=uuid.uuid4, primary_key=True)
group_id = Column(GUID, nullable=False, primary_key=True)
hierarchy_id = Column(GUID, nullable=False)
parent_id = Column(GUID)
seq = Column(Integer)
__table_args__ = (
ForeignKeyConstraint(
['parent_id', 'group_id'],
['node.id', 'node.group_id']
),
ForeignKeyConstraint(
['hierarchy_id', 'group_id'],
['hierarchy.id', 'hierarchy.group_id']
),
ForeignKeyConstraint(
['group_id'],
['group.id']
),
)
group = relationship(Group)
Group.hierarchies = relationship(
Hierarchy, backref="group", passive_deletes=True,
order_by='Hierarchy.title')
Hierarchy.nodes_all = relationship(
Node, backref='hierarchy', passive_deletes=True,
primaryjoin=and_(foreign(Node.hierarchy_id) == Hierarchy.id,
Node.group_id == Hierarchy.group_id))
Hierarchy.nodes = relationship(
Node, passive_deletes=True,
order_by=Node.seq, collection_class=ordering_list('seq'),
primaryjoin=and_(and_(foreign(Node.hierarchy_id) == Hierarchy.id,
Node.group_id == Hierarchy.group_id),
Node.parent_id == None))
Node.parent = relationship(
Node, backref=backref(
'children', passive_deletes=True,
order_by=Node.seq, collection_class=ordering_list('seq')),
primaryjoin=and_(foreign(Node.parent_id) == remote(Node.id),
Node.group_id == remote(Node.group_id)))
结构是:
node.parent_id is NULL
;这是filtering primary join)node.parent_id = remote(node.parent_id)
)答案 0 :(得分:1)
事实证明我将对象添加到子项列表中两次。删除node.hierarchy = hierarchy
行可解决问题。 然而,这是次优解决方案,因为我希望它很容易设置非根节点的层次结构。真正的解决方法是阻止Node.hierarchy
关系将项插入Hierarchy.nodes
列表。应该改变关系以使用单向反射:
Node.hierarchy = relationship(
Hierarchy,
primaryjoin=and_(foreign(Node.hierarchy_id) == remote(Hierarchy.id),
Node.group_id == remote(Hierarchy.group_id)))
Hierarchy.nodes = relationship(
Node, back_populates='hierarchy', passive_deletes=True,
order_by=Node.seq, collection_class=ordering_list('seq'),
primaryjoin=and_(and_(foreign(Node.hierarchy_id) == Hierarchy.id,
Node.group_id == Hierarchy.group_id),
Node.parent_id == None))
Linking Relationships with Backref > One Way Backrefs下的文档对此进行了描述。正如文档所指出的那样,这也会阻止项目被插入nodes
列表,即使Node.parent_id == None
也是如此,这有点不幸。
我还决定摆脱Hierarchy.nodes_all
关系,因为这只是让问题复杂化。如果我需要,我会使用特殊查询。