On 13/09/10 18:21, Michael Bayer wrote:
On Sep 13, 2010, at 12:26 PM, Jon Siddle wrote:
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?
because you didn't specify it, and it takes a palpable amount of additional
overhead to do so
I don't see why it's more overhead than an assignment child.parent = ...
at the same time as the list append parent.children.append(...). There's
obviously something more complex going on behind the scenes.
as well as a palpable amount of complexity to decide if it should do so based
on the logic you'd apply here, when in 99% of the cases it is not needed.
I just don't see the complexity of the logic here. I've specified I want
to join parent to each child, and it's already doing so in one
direction. I realise this is only a problem
for detached objects, but it leads to quite confusing behaviour, I think.
I don't see how this is wasteful, but I may be missing something.
Child may have parent, foo, bar, bat attached to it, all many-to-ones. Which
ones should it assume the user wants to load ?
parent. Because I have explicitly asked it to using "joinedload" or
"eagerload".
If you are loading 10000 rows, and each Child object has three many-to-ones
on it, and suppose it takes 120 function calls to look at a relationship,
determine the values to send to query._get(), look in the identity map, etc.,
that is 3 x 10000 x 120 = 3.6 million function calls
But you don't have to look in the identity map at all, since you've just
set the parent-child association in the other direction and thus have
both entities to hand, right?
, by default, almost never needed since normally they are all just there in the
current session, without the user asking to do so. There is nothing special
about Child.parent just because Parent.children is present up the chain.
While Hibernate may have decided that the complexity and overhead of adding
this decisionmaking was worth it, they have many millions more function calls
to burn with the Java VM in any case than we do in Python, and they also have a
much higher bar to implement lazyloading since their native class
instrumentation is itself a huge beast. In our case it is simply unnecessary.
Any such automatic decisionmaking you can be sure quickly leads to many
uncomfortable edge cases and thorny situations, causing needless surprise
issues for users who don't really need such a behavior in the first place.
I would agree with all of this if I understood why a) it takes an
appreciable number of function calls or b) "automatic decisionmaking" is
necessary. I don't think there's any ambiguity here, but again; perhaps
I'm missing something fundamental.
As I've mentioned, you will have an option to tell it which many-to-ones you'd like it to
spend time pre-populating using the upcoming "immedateload" option.
I still think this can be done with negligible overhead if it's done at
the same time as the other side of the relation (parent->child). Perhaps
I'll have to dig around in the code to see why this is such a problem.
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,
I disagree with this interpretation of "abstraction". That's like saying that
pushing the button in an elevator means you're now in charge of starting up the elevator
motors and instructing them how many feet to travel.
Huh? I didn't use the word "abstraction".
The template is not "doing" database operations, it is working with high level
objects that you've sent it, and knows nothing of a database. That these objects may be
doing database calls behind the scenes to lazily fetch additional data is known as the
proxy pattern. It is one of the most fundamental patterns in object oriented software
design. Separation of concerns is about what kinds of source code and awareness of
systems live in various places - it has nothing to do operational timing or initiation.
These operations may be "magic", but they're not transparent. They can
fail and/or have a potentially huge overhead. This is often intollerable
inside a view template. It's so much nicer to have a dumb and fast web
template with no nasty surprises like n+1 selects. This is orthogonal to
SoC.
The "pre-load" scenario is certainly valid if you're trying to render from an object
graph that loads from a cache and doesn't want to do any additional database calls. But this is
strictly an issue of optimization, not "correct" software design.
I'm not arguing that it's the only "correct" way, but I do think it has
benefits.
If you can point out what I'm missing that makes this so difficult, I'd
be interested; or I may get a chance to look through the code at some
point. Regardless, you've provided a practical solution to my problem
(contains_eager) that seems to be working well.
Thanks
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.