On 11/25/2015 01:32 PM, Christopher Lee wrote: > Looks like the problem is that self.entity is the root node and contains > the subclasses, so attempting to retrieve an attribute by class name > only works if it is a subclass. It also needs to handle the case where > self.entity is the mapped class you want.
yup I've updated the issue thanks! > > On Wed, Nov 25, 2015 at 10:26 AM, Christopher Lee <[email protected] > <mailto:[email protected]>> wrote: > > 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] > <mailto:[email protected]>> wrote: > > Verified that the patch works. > > On Wed, Nov 25, 2015 at 10:16 AM, Christopher Lee > <[email protected] <mailto:[email protected]>> wrote: > > Great, I'll try it out. > > Thanks, Mike! > > > On Tue, Nov 24, 2015 at 10:15 PM, Mike Bayer > <[email protected] <mailto:[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]> > >>> <mailto:[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]> > <mailto:[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:sqlalchemy%[email protected] > <mailto:sqlalchemy%[email protected]>> > >>> > > <mailto:[email protected] > <mailto:sqlalchemy%[email protected]> > >>> > <mailto:sqlalchemy%[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]>> > >>> > <mailto:[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]> > >>> > <mailto:sqlalchemy%[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]> > >>> <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.
