Load options aren't looking at the superclass

Issue #3287 resolved
malthe created an issue

If a query is made on an alias then the load_only option fails to exclude properties that are set on a base class of the aliased mapper.

q = sess.query(alias).options(load_only("age"))

This is demonstrated in a test case here: https://github.com/malthe/sqlalchemy/tree/load-only-with-alias-issue

Comments (14)

  1. malthe reporter

    Actually I failed to reproduce the actual problem in this test case.

    But there is an issue which is that the alias results in a subselect that lists all the columns in the mapper even though they're not part of the column selection:

    select a, b from (select a, b, c from foo join bar on ...)
    

    Note how c is not in the result, but it's still being listed in the subselect.

  2. Mike Bayer repo owner

    it's time to provide a real test case :). Here's a start:

    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)
    
    
    e = create_engine("sqlite://", echo=True)
    Base.metadata.create_all(e)
    
    s = Session(e)
    
  3. Mike Bayer repo owner

    OK, those tests can be helpful when I put the fix in but I need to understand the issue first (I'm fixing something else right now).

  4. malthe reporter

    Here's a snippet that does demonstrate the issue:

    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)
        a_name = Column(String)
    
    
    class B(A):
        __tablename__ = 'b'
    
        id = Column(Integer, ForeignKey("a.id"), primary_key=True)
        b_name = Column(String)
    
    
    class C(Base):
        __tablename__ = 'c'
    
        id = Column(Integer, primary_key=True)
        c_name = Column(String)
        b_id = Column("b_id", Integer, ForeignKey("b.id"))
    
        b = relationship(B)
    
    e = create_engine("sqlite://", echo=True)
    Base.metadata.create_all(e)
    
    s = Session(e)
    
    b = B(id=1, a_name="foo", b_name="bar")
    s.add(b)
    
    c = C(id=1, b_id=b.id, c_name="boo")
    s.add(c)
    
    s.flush()
    
    print s.query(C).join(B).options(
        contains_eager(C.b).load_only("id")
    )
    

    Note how a.a_name is included, but b.b_name is correctly excluded.

  5. Mike Bayer repo owner

    ok, so is this the bug?

    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)
        a_name = Column(String)
    
    class B(A):
        __tablename__ = 'b'
    
        id = Column(Integer, ForeignKey("a.id"), primary_key=True)
        b_name = Column(String)
    
    
    s = Session()
    
    print s.query(B).options(load_only(B.id))
    

    it isn't looking at the superclass. can we rename this issue then? This is all I'm looking at so far.

    SELECT b.id AS b_id, a.id AS a_id, a.a_name AS a_a_name 
    FROM a JOIN b ON a.id = b.id
    
  6. Mike Bayer repo owner
    • The "wildcard" loader options, in particular the one set up by the :func:.orm.load_only option to cover all attributes not explicitly mentioned, now takes into account the superclasses of a given entity, if that entity is mapped with inheritance mapping, so that attribute names within the superclasses are also omitted from the load. Additionally, the polymorphic discriminator column is unconditionally included in the list, just in the same way that primary key columns are, so that even with load_only() set up, polymorphic loading of subtypes continues to function correctly. fixes #3287

    → <<cset b63aae2c232f>>

  7. Mike Bayer repo owner
    • The "wildcard" loader options, in particular the one set up by the :func:.orm.load_only option to cover all attributes not explicitly mentioned, now takes into account the superclasses of a given entity, if that entity is mapped with inheritance mapping, so that attribute names within the superclasses are also omitted from the load. Additionally, the polymorphic discriminator column is unconditionally included in the list, just in the same way that primary key columns are, so that even with load_only() set up, polymorphic loading of subtypes continues to function correctly. fixes #3287

    (cherry picked from commit b63aae2c232f980a47aa2a635c35dfa45390f451)

    Conflicts: lib/sqlalchemy/orm/mapper.py

    → <<cset 11383dae8d14>>

  8. Log in to comment