On Mar 25, 2014, at 12:16 PM, Philip Scott <safetyfirstp...@gmail.com> wrote:

> I understand, I had a feeling it would be something like that. Don't worry, I 
> can work around it by using a subqueryload() instead (which I guess fixes it 
> by changing the order things are loaded?)

I'd have to look into it, I'm not sure why offhand.



> 
> Thank you very much for taking the time to answer; still loving SQLAlchemy!
> 
> 
> On Tue, Mar 25, 2014 at 1:06 PM, Michael Bayer <mike...@zzzcomputing.com> 
> wrote:
> 
> On Mar 25, 2014, at 7:48 AM, Philip Scott <safetyfirstp...@gmail.com> wrote:
> 
>> Ah, so, it turns out to be more subtle than I first thought. It took me 
>> quite a while to narrow it down to an easily reproducible case. To trigger 
>> the behavior you need to be: joinedloading() along a backref, and also I 
>> think it matters that I am joining back onto the same table and returning a 
>> bunch of objects. Quite a corner case I think.
>> 
>> This code demonstrates the behavior - it issues a second query in the for 
>> loop for the 'A' which has no child.
> 
> 
> this is very difficult to resolve and it may have been something I've said 
> was unfixable in the past.  The "a2" object here is loaded in two different 
> contexts, one is as the joined loaded child of a1, the other as a first class 
> result.   Because when the query orders by a1, a2, we hit a1 first, a2 is 
> necessarily loaded as the child of a1.  The joined loading only goes one 
> level deep, that is, it doesn't load the children of children, unless you 
> told it to by saying joinedload("child").joinedload("child").  So a2.child's 
> loader is declared as "not loaded".     then on the next row it comes in as a 
> first class result, but the a2 object we get there is only an identity map 
> lookup - this object is already loaded.
> 
> Adjusting this behavior would require the loading logic figure out 
> mid-results that the context for a particular object is changing.  pretty 
> complicated.
> 
> 
>> 
>> 
>> 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)
>> 
>>     join_table = Table('parent_child', Base.metadata,
>>                         Column('id_a', ForeignKey('a.id')),
>>                         Column('id_b', ForeignKey('a.id')))
>> 
>>     parent = relationship("A",
>>                       secondary=join_table,
>>                       primaryjoin = (id == join_table.c.id_a),
>>                       secondaryjoin = (id == join_table.c.id_b),
>>                       uselist=False,
>>                       backref=backref("child", uselist=False))
>> 
>> e = create_engine("sqlite://", echo=True)
>> Base.metadata.create_all(e)
>> 
>> sess = Session(e)
>> a1 = A()
>> a2 = A(parent=a1)
>> sess.add(a1)
>> sess.add(a2)
>> sess.commit()
>> sess.close()
>> 
>> results = sess.query(A).options(joinedload("child")).all()
>> print "----"
>> for a in results:
>>     print a.child is None
>> 
>> 
>> 
>> 
>> 
>> On Mon, Mar 24, 2014 at 8:34 PM, Michael Bayer <mike...@zzzcomputing.com> 
>> wrote:
>> 
>> On Mar 24, 2014, at 2:09 PM, Philip Scott <safetyfirstp...@gmail.com> wrote:
>> 
>> >
>> > Is this a bug, or perhaps some expected side effect of the joined load?
>> 
>> seemed like something that might be possible but the scalar loader is 
>> initializing the attribute to None to start with, here's a simple test that 
>> doesn't show your behavior, so see if you can just modify this one to show 
>> what you are seeing.  note we only need to see that 'bs' is in a1.__dict__ 
>> to prevent a 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", secondary=Table('atob', Base.metadata,
>>                         Column('aid', ForeignKey('a.id')),
>>                         Column('bid', ForeignKey('b.id'))
>>                     ),
>>             uselist=False)
>> 
>> class B(Base):
>>     __tablename__ = 'b'
>> 
>>     id = Column(Integer, primary_key=True)
>> 
>> e = create_engine("sqlite://", echo=True)
>> Base.metadata.create_all(e)
>> 
>> sess = Session(e)
>> sess.add(A())
>> sess.commit()
>> sess.close()
>> 
>> a1 = sess.query(A).options(joinedload("bs")).first()
>> assert 'bs' in a1.__dict__
>> assert a1.__dict__['bs'] is None
>> assert a1.bs is None
>> 
>> 
>> 
>> --
>> 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 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 sqlalchemy+unsubscr...@googlegroups.com.
>> To post to this group, send email to sqlalchemy@googlegroups.com.
>> 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 sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> 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 sqlalchemy+unsubscr...@googlegroups.com.
> To post to this group, send email to sqlalchemy@googlegroups.com.
> 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 sqlalchemy+unsubscr...@googlegroups.com.
To post to this group, send email to sqlalchemy@googlegroups.com.
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.

Reply via email to