Hmm, the patch seems to fix the case where the relationship is on the
subclass, but putting the relationship on the base class is resulting in an
AttributeError.
Node.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,
)
Node.children = sa.ext.associationproxy.association_proxy(
'links', 'child',
creator=lambda child: Edge(child_id=child.element_id))
Results in the stack trace:
Traceback (most recent call last):
File "./sqlalchemy_todo_test.py", line 108, in <module>
new_n1 =
session.query(NodeWithEdges).filter(NodeWithEdges.element_id==1).first()
File
"/home/chris/Documents/cozi/kits/output/site-packages/SQLAlchemy-1.0.9-py2.7-linux-x86_64.egg/sqlalchemy/orm/query.py",
line 2469, in first
ret = list(self[0:1])
File
"/home/chris/Documents/cozi/kits/output/site-packages/SQLAlchemy-1.0.9-py2.7-linux-x86_64.egg/sqlalchemy/orm/query.py",
line 2292, in __getitem__
return list(res)
File
"/home/chris/Documents/cozi/kits/output/site-packages/SQLAlchemy-1.0.9-py2.7-linux-x86_64.egg/sqlalchemy/orm/query.py",
line 2567, in __iter__
context = self._compile_context()
File
"/home/chris/Documents/cozi/kits/output/site-packages/SQLAlchemy-1.0.9-py2.7-linux-x86_64.egg/sqlalchemy/orm/query.py",
line 3029, in _compile_context
strategy(*rec[1:])
File
"/home/chris/Documents/cozi/kits/output/site-packages/SQLAlchemy-1.0.9-py2.7-linux-x86_64.egg/sqlalchemy/orm/strategies.py",
line 1329, in _create_eager_join
_entity_for_mapper(self.parent)
File
"/home/chris/Documents/cozi/kits/output/site-packages/SQLAlchemy-1.0.9-py2.7-linux-x86_64.egg/sqlalchemy/orm/util.py",
line 540, in _entity_for_mapper
return getattr(self.entity, mapper.class_.__name__)._aliased_insp
File
"/home/chris/Documents/cozi/kits/output/site-packages/SQLAlchemy-1.0.9-py2.7-linux-x86_64.egg/sqlalchemy/orm/util.py",
line 398, in __getattr__
raise AttributeError(key)
AttributeError: Node
On Wed, Nov 25, 2015 at 10:21 AM, Christopher Lee <[email protected]> wrote:
> Verified that the patch works.
>
> On Wed, Nov 25, 2015 at 10:16 AM, Christopher Lee <[email protected]>
> wrote:
>
>> Great, I'll try it out.
>>
>> Thanks, Mike!
>>
>>
>> On Tue, Nov 24, 2015 at 10:15 PM, Mike Bayer <[email protected]>
>> wrote:
>>
>>>
>>>
>>> On 11/24/2015 08:19 PM, Mike Bayer wrote:
>>> > I've bisected it down and this one is going to be tough.
>>> >
>>> https://bitbucket.org/zzzeek/sqlalchemy/issues/3593/eager-loading-single-inh-polymorphic-join
>>> > is added.
>>>
>>>
>>> I've figured out exactly where a simple thing was going wrong with this
>>> and produced a good patch that is safe to commit. Even though this
>>> use case broke between 0.8.0 and 0.8.1, the new approach begun in 0.8.1
>>> on forward removes most of the guessing from how the ORM joins things
>>> together and this fix just closes up a loose end, whereas the use case
>>> working in 0.8.0 and previously was because the old approach of guessing
>>> how to join things just happened to make the right guess here.
>>>
>>> I will try to add the necessary tests for this tomorrow so that it's
>>> good for 1.0.10 at least, feel free to try the patch ahead of time to
>>> confirm it resolves all your issues.
>>>
>>>
>>>
>>>
>>> >
>>> >
>>> >
>>> > On 11/24/2015 07:20 PM, Mike Bayer wrote:
>>> >>
>>> >>
>>> >> On 11/24/2015 07:16 PM, Christopher Lee wrote:
>>> >>> The test passes for me in 0.8.0. (Yes, we have 0.8.0 running in
>>> >>> production, and are finally getting around to upgrading... sigh...)
>>> >>
>>> >>
>>> >> agree. and it then fails in 0.8.1, so this is something very
>>> immediate
>>> >> and old in the 0.8 series I'll start looking to see the actual
>>> >> condition here.
>>> >>
>>> >>
>>> >>
>>> >>>
>>> >>>
>>> >>> On Tue, Nov 24, 2015 at 4:09 PM, Christopher Lee <[email protected]
>>> >>> <mailto:[email protected]>> wrote:
>>> >>>
>>> >>> I'll see if I can nail down the repro; I had to abstract some
>>> >>> production code that I am migrating between versions, and I may
>>> have
>>> >>> lost some important detail.
>>> >>>
>>> >>> Looking at the SQL it generates before and after moving the
>>> "links"
>>> >>> relationship, it appears that it is outer joining against the
>>> wrong
>>> >>> target, which explains why the identity map is getting confused.
>>> >>> (e.g., it is joining against the innermost anon_1 subquery in
>>> the
>>> >>> bad case, and against the todo_elements_1 reference in the good
>>> >>> case, which is why the good case is forming a nice tree.)
>>> >>>
>>> >>> On Tue, Nov 24, 2015 at 3:49 PM, Mike Bayer
>>> >>> <[email protected] <mailto:[email protected]>>
>>> wrote:
>>> >>>
>>> >>>
>>> >>>
>>> >>> On 11/24/2015 06:14 PM, Christopher Lee wrote:
>>> >>> >
>>> >>> > I am having a problem with SQLAlchemy 1.0.9 that cropped
>>> up when I
>>> >>> > upgraded from 0.8.
>>> >>>
>>> >>> this is a nicely written test but I get the same recursion
>>> overflow
>>> >>> error when I run it in 0.8, 0.9, and 1.0, even back in 0.8.3.
>>> >>> Can you
>>> >>> provide a script that illustrates success in 0.8 and not in
>>> 1.0
>>> >>> ? thanks.
>>> >>>
>>> >>>
>>> >>>
>>> >>>
>>> >>>
>>> >>>
>>> >>> >
>>> >>> > 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 [email protected]
>>> >>> <mailto:sqlalchemy%[email protected]>
>>> >>> > <mailto:[email protected]
>>> >>> <mailto:sqlalchemy%[email protected]>>.
>>> >>> > To post to this group, send email to
>>> [email protected] <mailto:[email protected]>
>>> >>> > <mailto:[email protected] <mailto:
>>> [email protected]>>.
>>> >>> > Visit this group at
>>> http://groups.google.com/group/sqlalchemy.
>>> >>> > For more options, visit https://groups.google.com/d/optout
>>> .
>>> >>>
>>> >>> --
>>> >>> 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 [email protected]
>>> >>> <mailto:sqlalchemy%[email protected]>.
>>> >>> To post to this group, send email to
>>> [email protected]
>>> >>> <mailto:[email protected]>.
>>> >>> Visit this group at
>>> http://groups.google.com/group/sqlalchemy.
>>> >>> For more options, visit https://groups.google.com/d/optout.
>>> >>>
>>> >>>
>>> >>>
>>> >>> --
>>> >>> 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 [email protected]
>>> >>> <mailto:[email protected]>.
>>> >>> To post to this group, send email to [email protected]
>>> >>> <mailto:[email protected]>.
>>> >>> Visit this group at http://groups.google.com/group/sqlalchemy.
>>> >>> For more options, visit https://groups.google.com/d/optout.
>>> >>
>>> >
>>>
>>> --
>>> 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 [email protected].
>>> To post to this group, send email to [email protected].
>>> Visit this group at http://groups.google.com/group/sqlalchemy.
>>> For more options, visit https://groups.google.com/d/optout.
>>>
>>
>>
>
--
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 [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.