On Sep 13, 2010, at 8:48 AM, Jon Siddle wrote:

> I'm sure I'm missing something simple here, and any pointers in the right 
> direction would be greatly appreciated.
> 
> Take for instance the following code:
> 
> session = Session()
> parents = session.query(Parent).options(joinedload(Parent.children)).all()
> session.close()
> 
> print parents[0].children  # This works
> print parents[0].children[0].parent  # This gives a lazy loading error
> 
> Adding the following loop before closing the session works (and doesn't hit 
> the DB):
> 
> for p in parents:
>  for c in p.children:
>    c.parent
> 
> As far as I can tell, the mapping is correct since:
> 
> * It all works fine if I leave the session open
> * If I don't use joinedload, and leave the session open it lazyloads correctly
> 
> I'm surprised that:
> 
> * It doesn't set both sides of the relation, considering it apparently knows 
> about them

This relationship is satisfied as you request it, and it works by looking in 
the current Session's identity map for the primary key stored by the 
many-to-one.   The operation falls under the realm of "lazyloading" even though 
no SQL is emitted.   If you consider that Child may have many related 
many-to-ones, all of which may already be in the Session, it would be quite 
wasteful for the ORM to assume that you're going to be working with the object 
in a detached state and that you need all of them.

The Session's default assumption is that you're going to be leaving it around 
while you work with the objects contained, and in that way you interact with 
the database for as long as you deal with its objects, which represent 
"proxies" to the underlying transaction.   When objects are detached, for 
reasons like caching and serialization to other places, normally you'd merge() 
them back when you want to use them again.   So if it were me I'd normally be 
looking to not be closing the session.

However, when working with detached objects is necessary, two approaches here 
you can use.  One is a general approach that can load anything related, which 
is to load them in a @reconstructor.  This is illustrated at  
http://www.sqlalchemy.org/trac/wiki/UsageRecipes/ImmediateLoading .    It won't 
issue any extra SQL for the many-to-ones that are present in the session 
already.

In the specific case you have above, you can also use a trick which is to use 
contains_eager():

parents = session.query(Parent).options(joinedload(Parent.children), 
contains_eager(Parent.children, Child.parent)).all()

the above approach requires that Parent is one of the entities that you're 
requesting explicitly - i.e. if you were saying joinedload("foo", "bar", 
"bat"), it would be kind of impossible to target "bat.hohos" with 
contains_eager() due to the aliasing.







this will do the get() of the Parent as you run through.

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to