On 5/23/15 5:45 PM, Alex S wrote:
When I start printing some user's relations (u.preferences or
u.accounts), I've found that all data was load via q.all() call and
there are no additional queries in database. All data are loaded
except for u.preferences.current_account.preferences. Account
preferences for current account weren't loaded on q.all() stage and
the additional sql query was issued when printing
u.preferences.current_account.preferences. This behaviour is not
suitable for me. Could anyone please tell me where I'm wrong? Are
there any ways to resolve the issue?
thank you very much for providing a complete, succinct and easy to read
test case for this, it is becoming very rare that I get the actual code
that runs and makes the behaviors clear instead of fragments and hints
that leave me guessing.
This one is hard to pin down because it comes and goes based on hash
ordering, but in an extremely weird way that it is hard to reproduce
consistently.
But at the end of the day it comes down to how the Account object that
happens to be present at u.preferences.current_account was loaded.
Before rows come in, we iterate through the attributes associated with
the User object and set up functions that will load each attribute.
For each of those attributes, we do the same thing in a recursive fashion.
The joined eager loaders set things up in such a way that when they are
asked to load an object from the row, if that object is already present
in the identity map, we assume it is already loaded and we don't touch
the rest of the attributes.
In this case, if I put some printing inside the loading routine where we
first look at User (and also some randomizing of the order), if the
order of the attributes processed is this:
['email', 'preferences', 'id', 'accounts']
we don't get the SQL on u.preferences.current_account.preferences. This
is because we run the loader for "u.preferences" first, which means we
run "u.preferences.current_account" that produces an Account object, we
then run all the loaders for that Account and we hit the
"u.preferences.current_account.preferences" loader and see that there is
data in the row, and we set up AccountPreferences.
If the order of the attributes processed is this:
['accounts', 'email', 'preferences', 'id']
we *do* get the SQL on u.preferences.current_account.preferences. This
is because we run the loader for "u.accounts", we find the Account
object, we then run all the loaders for that Account *at that path* and
find there is no loader for "Account.preferences". We leave it unloaded,
put the Account into the identity map. Later, we run the loader for
"u.preferences", go through the process where we hit
"u.preferences.current_account", see that the Account identity is in the
row, but we check the identity map and the Account is already there. We
assume it was loaded elsewhere and we don't touch it. We never look at
the "u.preferences.current_account.preferences" loader.
There's some options to change this behavior but in this case they don't
work, because of the mechanics of dealing with multiple parts of the
same row within a result set.
There isn't currently a system to set up "precedence" of these
loaders. It might be possible someday but it would be extremely
complicated to figure out. Just to write this email took me about 40
minutes to figure out exactly.
The best I can do is that there's a patch that can make this work but I
definitely cannot commit it in 1.0; the eager loading mechanics are
extremely sensitive and easily messed up by even the most subtle change,
and I'd have to find time to think and test this very deeply and at best
it would be for 1.1. That issue is at
https://bitbucket.org/zzzeek/sqlalchemy/issue/3431/object-eager-loaded-via-two-separate-paths.
--
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 [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.