Creando relación de contenedor en SQLAlchemy declarativa

La aplicación My Python / SQLAlchemy administra un conjunto de nodos, todos derivados de un nodo de clase base. Estoy usando las funciones de polymorphism de SQLAlchemy para administrar los nodos en una tabla SQLite3. Aquí está la definición de la clase de nodo base:

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} 

y, como ejemplo, una de las clases derivadas, 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) 

Ahora necesito un nuevo tipo de nodo, ListNode , que es un contenedor ordenado de cero o más Nodos . Cuando cargue un ListNode , quiero que tenga su ID y título (de la clase de Nodo base) junto con una colección de sus nodos (secundarios) contenidos. Un nodo puede aparecer en más de un ListNode , por lo que no es una jerarquía adecuada. Los crearía a lo largo de estas líneas:

 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) 

La lista de elementos secundarios solo debería consistir en objetos de Nodo , es decir, todo lo que necesito es su clase de base. No se deben realizar plenamente en las clases especializadas (por lo que no obtengo el gráfico completo de una vez, entre otras razones).

Comencé por las siguientes líneas, juntando fragmentos y piezas que encontré en varios lugares sin una comprensión completa de lo que estaba sucediendo, por lo que puede que esto no tenga mucho sentido:

 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") 

Este enfoque falla de varias maneras: no parece permitir un ListNode vacío, y al establecer el atributo de los elementos en una lista, SQLAlchemy se queja de que el objeto ‘list’ no tiene atributo ‘_sa_instance_state’. No es sorprendente que las horas de mutaciones aleatorias sobre este tema no hayan dado buenos resultados,

Tengo experiencia limitada en SQLAlchemy pero realmente quiero que esto funcione pronto. Apreciaría mucho cualquier consejo o dirección que pueda ofrecer. ¡Gracias por adelantado!

Necesita una tabla adicional para la relación de muchos a muchos:

 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) 

Actualización : a continuación hay un ejemplo de lista ordenada usando 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))