On 13/09/10 16:45, Michael Bayer wrote:
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.
I'm not sure I see what you're saying here. I've explicitly asked for "all children relating to parent" and these are correctly queried and loaded. While they are being added to the parent.children list, why not also set each child.parent since this is known? I don't see how this is wasteful, but I may be missing something. I'm not suggesting it should touch relations that I haven't explicitly told it to eagerly load. The likes of Hibernate (yes, it's a very different beast) load both sides of the relation at once.
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.
I'm closing the session before I forward the objects to the view template in a web application. The template has no business doing database operations, and the controller *should* make sure all DB work has been done. In my case, I know I'll never need to write back
to the DB.
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()
This seems to address my problem directly. It's still a bit redundant, but from my initial tests it seems to solve my problem.
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.
I'm only interested in making sure both sides of the same relation are loaded; so this isn't a problem at all.

so let me also back that up, that we've always planned on adding an "immediateload" 
option that would just fire off any lazyloader as the query fetches results.    A really short 
patch that adds "immediateload()" is athttp://www.sqlalchemy.org/trac/ticket/1914  and 
hopefully will be in 0.6.5 pending further testing.
We'll have to support 0.5 for some time, but it's good to know a shortcut is coming.

Thanks a lot for your help.

Jon

--
Jon Siddle, CoreFiling Limited
Software Tools Developer
http://www.corefiling.com
Phone: +44-1865-203192

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

Reply via email to