Python 在声明式SQLAlchemy中创建容器关系

Python 在声明式SQLAlchemy中创建容器关系,python,sqlalchemy,Python,Sqlalchemy,我的Python/SQLAlchemy应用程序管理一组节点,所有节点都派生自基类节点。我正在使用SQLAlchemy的多态性特性来管理 SQLite3表中的节点。下面是基本节点类的定义: 作为示例,其中一个派生类NoteNode: 现在我需要一种新的节点,ListNode,它是一个由零个或多个节点组成的有序容器。加载ListNode时,我希望它具有ID和标题 (来自基本节点类)及其包含(子)节点的集合。一个节点可能出现在多个ListNode中,因此它不是一个适当的层次结构。我将按照以下思路创建它

我的Python/SQLAlchemy应用程序管理一组节点,所有节点都派生自基类节点。我正在使用SQLAlchemy的多态性特性来管理 SQLite3表中的节点。下面是基本节点类的定义:

作为示例,其中一个派生类NoteNode:

现在我需要一种新的节点,ListNode,它是一个由零个或多个节点组成的有序容器。加载ListNode时,我希望它具有ID和标题 (来自基本节点类)及其包含(子)节点的集合。一个节点可能出现在多个ListNode中,因此它不是一个适当的层次结构。我将按照以下思路创建它们:

note1 = NoteNode(title=u"Note 1", content_type="text/text", content=u"I am note #1")
session.add(note1)

note2 = NoteNode(title=u"Note 2", content_type="text/text", content=u"I am note #2")
session.add(note2)

list1 = ListNode(title=u"My List")
list1.items = [note1,note2]
session.add(list1)
儿童名单只应包括 由节点对象组成——也就是说,我只需要它们的基类。他们不应该完全进入专业课程 (因此,除其他原因外,我无法立即获得整个图表)

我沿着下面的路线开始,把我在不同地方发现的零碎东西拼凑在一起,而对这些东西没有完全的了解 发生了什么,所以这可能没有多大意义:

class ListNode(Node):
    __mapper_args__ = {'polymorphic_identity': 'list', 'inherit_condition':id==Node.id}
    __tablename__ = 'nodes_list_contents'
    id = Column(None, ForeignKey('nodes.id'), primary_key=True)
    item_id = Column(None, ForeignKey('nodes.id'), primary_key=True)
    items = relation(Node, primaryjoin="Node.id==ListNode.item_id")
这种方法在以下几个方面失败:它似乎不允许空的ListNode,并且将items属性设置为list结果 在SQLAlchemy中,抱怨“list”对象没有属性“\u sa\u instance\u state”。毫不奇怪,数小时的随机突变并没有给这个主题带来任何影响 好成绩,

我在SQLAlchemy方面的经验有限,但我真的很想尽快让它发挥作用。如果您能给我任何建议或指导,我将不胜感激
提供提前谢谢

对于多对多关系,您需要一个额外的表:

nodes_list_nodes = Table(
    'nodes_list_nodes', metadata,
    Column('parent_id', None, ForeignKey('nodes_list.id'), nullable=False),
    Column('child_id', None, ForeignKey(Node.id), nullable=False),
    PrimaryKeyConstraint('parent_id', 'child_id'),
)

class ListNode(Node):
    __mapper_args__ = {'polymorphic_identity': 'list'}
    __tablename__ = 'nodes_list'
    id = Column(None, ForeignKey('nodes.id'), primary_key=True)
    items = relation(Node, secondary=nodes_list_nodes)
更新:下面是使用
关联\u代理的有序列表示例:

from sqlalchemy.orm.collections import InstrumentedList
from sqlalchemy.ext.associationproxy import association_proxy


class ListNodeAssociation(Base):
    __tablename__ = 'nodes_list_nodes'
    parent_id = Column(None, ForeignKey('nodes_list.id'), primary_key=True)
    child_id = Column(None, ForeignKey(Node.id), primary_key=True)
    order = Column(Integer, nullable=False, default=0)
    child = relation(Node)
    __table_args__ = (
        PrimaryKeyConstraint('parent_id', 'child_id'),
        {},
    )


class OrderedList(InstrumentedList):

    def append(self, item):
        if self:
            item.order = self[-1].order+1
        else:
            item.order = 1
        InstrumentedList.append(self, item)


class ListNode(Node):
    __mapper_args__ = {'polymorphic_identity': 'list'}
    __tablename__ = 'nodes_list'
    id = Column(None, ForeignKey('nodes.id'), primary_key=True)
    _items = relation(ListNodeAssociation,
                      order_by=ListNodeAssociation.order,
                      collection_class=OrderedList,
                      cascade='all, delete-orphan')
    items = association_proxy(
                '_items', 'child',
                creator=lambda item: ListNodeAssociation(child=item))

这将导致“items”是实际Item对象的列表,对吗?二进制关系最终是完全透明的,这样他就不需要associationproxy跳过它到实际对象,对吗?对,这种情况很简单。使用中间模型时需要associationproxy,即使列表有序(连接表中的添加顺序字段)。非常感谢Denis-这真的打破了僵局。如果我可以更进一步,我已经用声明式风格重写了关系表:class ListNodeAssociation(Base):tablename='nodes\u list\u nodes'parent\u id=Column(无,ForeignKey('nodes\u list.id'),primary\u key=True)child\u id=Column(无,ForeignKey(Node.id),primary\u key=True)我仍然需要关系井然有序,最好是通过SQLAlchemy“orderinglist”机制,但我不清楚它在这种情况下是如何应用的。。。它是否适用,如果适用,如何适用?您可以将原始问题正确地更新,并将新代码标记为更新。或者用更好的标题发布另一个问题(指这一个),这样其他人就可以很容易地找到它。
nodes_list_nodes = Table(
    'nodes_list_nodes', metadata,
    Column('parent_id', None, ForeignKey('nodes_list.id'), nullable=False),
    Column('child_id', None, ForeignKey(Node.id), nullable=False),
    PrimaryKeyConstraint('parent_id', 'child_id'),
)

class ListNode(Node):
    __mapper_args__ = {'polymorphic_identity': 'list'}
    __tablename__ = 'nodes_list'
    id = Column(None, ForeignKey('nodes.id'), primary_key=True)
    items = relation(Node, secondary=nodes_list_nodes)
from sqlalchemy.orm.collections import InstrumentedList
from sqlalchemy.ext.associationproxy import association_proxy


class ListNodeAssociation(Base):
    __tablename__ = 'nodes_list_nodes'
    parent_id = Column(None, ForeignKey('nodes_list.id'), primary_key=True)
    child_id = Column(None, ForeignKey(Node.id), primary_key=True)
    order = Column(Integer, nullable=False, default=0)
    child = relation(Node)
    __table_args__ = (
        PrimaryKeyConstraint('parent_id', 'child_id'),
        {},
    )


class OrderedList(InstrumentedList):

    def append(self, item):
        if self:
            item.order = self[-1].order+1
        else:
            item.order = 1
        InstrumentedList.append(self, item)


class ListNode(Node):
    __mapper_args__ = {'polymorphic_identity': 'list'}
    __tablename__ = 'nodes_list'
    id = Column(None, ForeignKey('nodes.id'), primary_key=True)
    _items = relation(ListNodeAssociation,
                      order_by=ListNodeAssociation.order,
                      collection_class=OrderedList,
                      cascade='all, delete-orphan')
    items = association_proxy(
                '_items', 'child',
                creator=lambda item: ListNodeAssociation(child=item))