eager loading, particiularly joined, should populate sub-attributes when the parent is already present

Issue #2213 resolved
Mike Bayer repo owner created an issue

attached patch illustrates kind of how this would have to work + some tests. Need to know impact on performance, what other gotchas come up, etc. it needs to move around how we do _populators() and all that.

Comments (4)

  1. Mike Bayer reporter

    next patch illustrates it checking for "eager + eager" in a somewhat vain attempt to avoid unnecessary eager loads. In particular the effect is more apparent with subquery loading rather than joined. At the moment, leaning towards:

    1. make sure subqueryloading just doesn't do this behavior. Having an existing object reload its subquery attributes over and over is not at all how that should work.
    2. joinedload, since it always has the columns available from the LEFT OUTER JOIN, can less controversially use these columns and scan existing objects.
    3. not certain yet if the entire behavior should only be explicit - query.load_all() or something like that.

    For the subqueryload issue, the following demonstrates how hitting the same B() repeatedly loads its C(), even with the second patch:

    from sqlalchemy import *
    from sqlalchemy.orm import *
    from sqlalchemy.ext.declarative import declarative_base
    
    Base = declarative_base()
    
    a_to_b = Table('a_to_b', Base.metadata,
        Column('a_id', Integer, ForeignKey('a.id')),
        Column('b_id', Integer, ForeignKey('b.id'))
    )
    
    class A(Base):
        __tablename__ = 'a'
        id = Column(Integer, primary_key=True)
        bs = relationship("B", secondary=a_to_b)
    
    class B(Base):
        __tablename__ = 'b'
        id = Column(Integer, primary_key=True)
        cs = relationship("C", lazy="subquery")
    
    class C(Base):
        __tablename__ = 'c'
        id = Column(Integer, primary_key=True)
        b_id = Column(Integer, ForeignKey('b.id'))
        ds = relationship("D", lazy="subquery")
    
    class D(Base):
        __tablename__ = 'd'
        id = Column(Integer, primary_key=True)
        c_id = Column(Integer, ForeignKey('c.id'))
    
    e = create_engine('sqlite://', echo=True)
    Base.metadata.create_all(e)
    s = Session(e)
    
    b = B(cs=[C(), C()](C(),))
    s.add_all([   A(bs=[b](
    )),
        A(bs=[b](b)),
    ])
    s.commit()
    s.close()
    
    for a in s.query(A):
        a.bs
    
  2. Mike Bayer reporter

    next patch looks like the feature. not sure if turning it on for 0.7.2 is the way to go.

  3. Log in to comment