I think I may have found the issue. If I modify your example so that "C.k1" is a declared_attr, the assertion on the second C instance loads the (100,1000) record from table "ab" again. I originally had my "k1" in a mixin, but it looks like the mixin isn't involved, and it is purely the declared_attr. I assume that the "k1" coming from the declared_attr doesn't have quite the right karma in some way when used in the ForeignKeyConstraint in C that makes the relationship to AB? I'll have to go back and dig into the docs, but am I using declared_attr decorator improperly, or is there a better way to refer to "k1" in the ForeignKeyConstraint?
My modified version of your test is below. Change the "useDeclared" variable to False to bypass the declared_attr behaviour and recover the working of the original test. from sqlalchemy import * from sqlalchemy.orm import * from sqlalchemy.ext.declarative import declarative_base, declared_attr useDeclared = True # set True to see unneeded select on #2 useMixin = False # put the declared in the mixin if True Base = declarative_base() class A(Base): __tablename__ = 'a' id = Column(Integer, primary_key=True) assoc = relationship("AB") class B(Base): __tablename__ = 'b' id = Column(Integer, primary_key=True) class AB(Base): __tablename__ = 'ab' a_id = Column(ForeignKey('a.id'), primary_key=True) b_id = Column(ForeignKey('b.id'), primary_key=True) a = relationship("A") b = relationship("B") class Mixin_C(object): if useMixin: @declared_attr def k1(cls): print "DECLARED K1" return Column(Integer) class C(Base, Mixin_C): __tablename__ = 'c' id = Column(Integer, primary_key=True) if not useMixin: if useDeclared: @declared_attr def k1(cls): print "declared without mixin" return Column(Integer) else: print "INLINE k1" k1 = Column(Integer) k2 = Column(Integer) assoc = relationship("AB") __table_args__ = ( ForeignKeyConstraint(['k1', 'k2'], ['ab.a_id', 'ab.b_id']), {}) e = create_engine("sqlite://", echo=True) Base.metadata.create_all(e) s = Session(e) a, b = A(id=100), B(id=1000) ab = AB(a=a, b=b) c1 = C(id=1, assoc=ab) c2 = C(id=2, assoc=ab) s.add_all([a, b, ab, c1, c2]) s.commit() s.close() # clears everything c1 = s.query(C).get(1) print "#1 EMITS LAZYLOAD:" assoc = c1.assoc # note we keep a strong reference here c2 = s.query(C).get(2) print "\n#2 SHOULD NOT EMIT LAZYLOAD%s"%("" if not useDeclared else ", but will because of declared_attr") assert c2.assoc is assoc -- 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 sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.