【问题标题】:Creating container relationship in declarative SQLAlchemy在声明性 SQLAlchemy 中创建容器关系
【发布时间】:2010-12-19 23:42:24
【问题描述】:

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

class Node(db.Base):
    __tablename__ = 'nodes'
    id = Column(Integer, primary_key=True)
    node_type = Column(String(40))
    title = Column(UnicodeText)
    __mapper_args__ = {'polymorphic_on': node_type}

例如,一个派生类,NoteNode

class NoteNode(Node):
    __mapper_args__ = {'polymorphic_identity': 'note'}
    __tablename__ = 'nodes_note'
    id = Column(None,ForeignKey('nodes.id'),primary_key=True)
    content_type = Column(String)
    content = Column(UnicodeText)

现在我需要一种新的节点,ListNode,它是零个或多个 Node 的有序容器。当我加载一个 ListNode 时,我希望它有它的 ID 和标题 (来自基础 Node 类)及其包含的(子)节点的集合。一个Node 可能出现在多个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)

孩子的名单应该只 由 Node 对象组成——也就是说,我所需要的只是它们的基类内容。他们不应该完全实现到专业课 (因此,除其他原因外,我没有立即获得整个图表)。

我从以下几行开始,将我在各个地方找到的零碎拼凑在一起,但没有完全了解 发生了什么,所以这可能没有多大意义:

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 属性设置为列表结果 在 SQLAlchemy 中抱怨“列表”对象没有属性“_sa_instance_state”。毫不奇怪,在这个主题上数小时的随机突变并没有给出任何 效果不错,

我在 SQLAlchemy 方面的经验有限,但我真的很想尽快完成这项工作。我非常感谢您提供的任何建议或指导 提供。提前致谢!

【问题讨论】:

    标签: python sqlalchemy


    【解决方案1】:

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

    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)
    

    更新:下面是使用association_proxy的有序列表示例:

    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 对象的列表,对吗?二元关系最终是完全透明的,因此他不需要关联代理来跳过它到实际对象,对吧?
    • 对,这个案子很简单。使用中间模型时需要关联代理,即使列表有序(连接表中的添加顺序字段)。
    • 非常感谢,丹尼斯 - 这真的打破了僵局。如果我可以进一步研究它,我已经以声明式样式重写了关系表: 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) 我仍然需要关系有序,最好是通过 SQLAlchemy 'orderinglist' 机制,但我不清楚它是如何实现的适用于这种情况...是否适用,如果适用,如何适用?
    • 您可以正确更新原始问题,将新代码标记为更新。或者用更好的标题发布另一个问题(参考这个问题),以便其他人可以轻松找到它。
    • 我为有序列表添加了一个示例。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-28
    • 2014-11-15
    • 1970-01-01
    • 2016-07-11
    • 2022-01-08
    • 2014-05-06
    • 2010-09-20
    相关资源
    最近更新 更多