- changed status to resolved
aliased vs. non-aliased specificity in loader options on lazy load
Issue #3963
resolved
all of these cases emit eager load for B.cs when they are specified as lazyload:
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class A(Base):
__tablename__ = 'a'
id = Column(Integer, primary_key=True)
bs = relationship("B")
class B(Base):
__tablename__ = 'b'
id = Column(Integer, primary_key=True)
a_id = Column(ForeignKey('a.id'))
cs = relationship("C")
class C(Base):
__tablename__ = 'c'
id = Column(Integer, primary_key=True)
b_id = Column(ForeignKey('b.id'))
e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)
s = Session(e)
s.add(A(bs=[B(cs=[C()])]))
s.add(A())
s.commit()
s.close()
aa = aliased(A)
q = s.query(aa, A).filter(aa.id == 1).filter(A.id == 2).options(joinedload(A.bs).joinedload(B.cs))
for a, real_a in q:
for b in a.bs:
# was eager loaded, when should lazy load
for c in b.cs:
print c
s.close()
print "--------------------------------"
aa = aliased(A)
q = s.query(aa, A).filter(aa.id == 1).filter(A.id == 2).options(defaultload(A.bs).joinedload(B.cs))
for a, real_a in q:
for b in a.bs:
# was eager loaded, when should lazy load
for c in b.cs:
print c
s.close()
print "--------------------------------"
aa = aliased(A)
q = s.query(A, aa).filter(A.id == 1).filter(aa.id == 2).options(defaultload(aa.bs).joinedload(B.cs))
for a, real_a in q:
for b in a.bs:
# was eager loaded, when should lazy load
for c in b.cs:
print c
_chop_path is important here, this fixes:
diff --git a/lib/sqlalchemy/orm/strategy_options.py b/lib/sqlalchemy/orm/strategy_options.py
index bae2b73..60ed2be 100644
--- a/lib/sqlalchemy/orm/strategy_options.py
+++ b/lib/sqlalchemy/orm/strategy_options.py
@@ -376,7 +376,8 @@ class _UnboundLoad(Load):
_WILDCARD_TOKEN,) and c_token != p_prop.key:
return None
elif isinstance(c_token, PropComparator):
- if c_token.property is not p_prop:
+ if c_token.property is not p_prop or \
+ c_token._parententity is not p_mapper:
return None
else:
i += 1
Comments (4)
-
reporter -
reporter - changed status to open
regression:
from sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.ext.declarative import declarative_base Base = declarative_base() class Milestone(Base): __tablename__ = 'milestone' id = Column(Integer, primary_key=True) milestone_type = Column(String(64), nullable=False) owner_id = Column( Integer, ForeignKey('owner.id', name='milestone_owner_id_owner_id_fkey')) owner = relationship('Owner', back_populates='milestone') __mapper_args__ = { 'polymorphic_on': milestone_type, } class Sprint(Milestone): __tablename__ = 'sprint' id = Column( Integer, ForeignKey('milestone.id', name='sprint_id_milestone_id_fkey'), primary_key=True) __mapper_args__ = { 'polymorphic_identity': 'sprint' } class Owner(Base): __tablename__ = 'owner' id = Column(Integer, primary_key=True) dept_id = Column( Integer, ForeignKey('dept.id', name='owner_dept_id_dept_id_fkey')) dept = relationship('Dept', back_populates='owner') milestone = relationship('Milestone', back_populates='owner') class Dept(Base): __tablename__ = 'dept' id = Column(Integer, primary_key=True) owner = relationship('Owner', back_populates='dept') e = create_engine("sqlite://", echo=True) Base.metadata.create_all(e) sess = Session(e) d = Dept(id=1) o = Owner(id=1) o.dept = d s = Sprint(id=1) s.owner = o sess.add(o) sess.commit() sess.query(Sprint).filter(Sprint.id.in_([1])).options( subqueryload(Sprint.owner).subqueryload(Owner.dept)).all()
the third query (which is also wrong in 1.1, separate bug) does not emit anymore
-
reporter - marked as critical
-
reporter - changed status to resolved
Repair regression to pathing for subclasses
Issue
#3963's initial commit narrowed the "current path" match rules too much such that a path that matches current path on subclass would no longer match.Change-Id: I8c9a0db91a09d789cfb8666288a913f8bbcdb2e9 Fixes:
#3963→ <<cset 770e1e399cb0>>
- Log in to comment
Compare entities also on chop_path
When comparing query._current_path to options, the path chop was not taking into account that the query or the options are against aliased classes that don't match the mapper.
The issue does not seem to take place for the Load() version of _chop_path.
Fixed bug to improve upon the specificity of loader options that take effect subsequent to the lazy load of a related entity, so that the loader options will match to an aliased or non-aliased entity more specifically if those options include entity information.
Fixes:
#3963Change-Id: Ifdff37d579042fcc62bdeabce9e2413e9a03fbba→ <<cset 86c43d2e0f0c>>