I am having a problem with SQLAlchemy 1.0.9 that cropped up when I upgraded
from 0.8.

I have the following polymorphic relationship defined.  For some reason,
when I build a tree, and I try to access the children of the middle node of
the tree, it is picking up the wrong edge object and going into a recursive
loop.  (Querying for the children of n2 is picking up the edge e12, instead
of the edges e23, e24 and e25.)

Looking at the identity map, it looks like the query isn't populating the
correct objects.

If I move the "links" relationship from the NodesWithEdges class to the
base Node class, the query works fine.  Also, if I drop join_depth or
remove with with_polymorphic mapper arg, it forces multiple queries, and
the problem doesn't occur.  Is this a bug with the new release, or am I
doing something bad?


from __future__ import print_function, unicode_literals

import sqlalchemy as sa
import sqlalchemy.orm
import sqlalchemy.ext.associationproxy
import sqlalchemy.ext.declarative
import sqlalchemy.ext.orderinglist

Base = sqlalchemy.ext.declarative.declarative_base()


class Node(Base):
    __tablename__ = 'todo_elements'

    element_id = sa.Column(sa.Integer, nullable=False, primary_key=True)
    element_type = sa.Column(sa.String(20), nullable=False)

    __mapper_args__ = {
        'polymorphic_on': element_type,
        'with_polymorphic': ('*', None),
    }


class NodeWithEdges(Node):
    __mapper_args__ = {'polymorphic_identity': 'todo.list'}


class LeafNode(Node):
    __mapper_args__ = {'polymorphic_identity': 'todo.item'}

    my_flag = sa.Column(sa.Boolean, default=False)


class Edge(Base):
    __tablename__ = 'todo_links'
    __table_args__ = (
        sa.PrimaryKeyConstraint('parent_id', 'child_id'),
        sa.ForeignKeyConstraint(['parent_id'], [Node.element_id]),
        sa.ForeignKeyConstraint(['child_id'], [Node.element_id]),
    )

    parent_id = sa.Column(sa.Integer, nullable=False)
    child_id = sa.Column(sa.Integer, nullable=False)


Edge.child = sa.orm.relationship(
    Node,
    uselist=False,
    primaryjoin=Edge.child_id == Node.element_id,
    lazy=False,
    cascade='all',
    passive_updates=False,
    join_depth=8,
)


NodeWithEdges.links = sa.orm.relationship(
    Edge,
    primaryjoin=NodeWithEdges.element_id == Edge.parent_id,
    lazy=False,
    cascade='all, delete-orphan',
    single_parent=True,
    passive_updates=False,
    join_depth=8,
)

NodeWithEdges.children = sa.ext.associationproxy.association_proxy(
    'links', 'child',
    creator=lambda child: Edge(child_id=child.element_id))



engine = sa.create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)

Session = sa.orm.sessionmaker(bind=engine)
session = Session()


#
# 1 --> 2 --> 3
#         --> 4
#         --> 5
#

n1 = NodeWithEdges(element_id=1)
n2 = NodeWithEdges(element_id=2)
n3 = LeafNode(element_id=3)
n4 = LeafNode(element_id=4, my_flag=True)
n5 = LeafNode(element_id=5)

e12 = Edge(parent_id=n1.element_id, child_id=n2.element_id)
e23 = Edge(parent_id=n2.element_id, child_id=n3.element_id)
e24 = Edge(parent_id=n2.element_id, child_id=n4.element_id)
e25 = Edge(parent_id=n2.element_id, child_id=n5.element_id)

session.add_all([n1, n2, n3, n4, n5, e12, e23, e24, e25])
session.commit()
session.expunge_all()

new_n1 = 
session.query(NodeWithEdges).filter(NodeWithEdges.element_id==1).first()
print(session.identity_map.keys())


def traverse(node, f, depth=0):
    f(node, depth)
    if hasattr(node, 'children'):
        for c in node.children:
            traverse(c, f, depth + 1)

def indent_print(node, depth):
    print('  ' * depth + str(node.element_id))
    if hasattr(node, 'my_flag'):
        print(node.my_flag)

traverse(new_n1, indent_print)

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to