On 9/2/15 10:22 PM, Dave Vitek wrote:
On 9/2/2015 10:08 PM, Mike Bayer wrote:
On 9/2/15 9:57 PM, Dave Vitek wrote:
Answering my own question.
Here's a patch that seems to fix it, but I am uncertain about
whether it breaks other things. Use at your own risk, at least
until someone more familiar with this code evaluates it.
this is a variant of a known issue, that of object X is being loaded
in different contexts in the same row, with different loader
strategies applied. The first context that hits it wins.
I'm adding your test case to that issue at
https://bitbucket.org/zzzeek/sqlalchemy/issues/3431/object-eager-loaded-on-scalar-relationship.
the patch which is there seems to fix this test as well - it is
extremely similar to your patch, so nice job!
I have a question about your patch. If "existing" is None and is not
present in dict_, it does not get placed into dict_. I note that
load_scalar_from_joined_new_row puts it into dict_ regardless of
whether it is None. Is this intentional/good?
it was intentional, as far as "good", that's part of why i didn't merge
this in too quickly; I usually have to spend a few hours deeply
re-familiarizing with sections like these before making assumptions.
the new/existing row thing has to do with the fact that the join returns
for us the parent object multiple times. the first time we see the
parent, it's "new". the subsequent times, it's "existing". So here we
are looking at a related item. If we call _instance() and get None
back, that means the columns for the related item were incomplete, e.g.
primary key is incomplete / missing. There shouldn't be a case where
the row is None for "new" and non-None for "existing", but this is based
on my not having re-familiarized with this section yet.
Index: lib/sqlalchemy/orm/strategies.py
===================================================================
--- lib/sqlalchemy/orm/strategies.py (revision 114304)
+++ lib/sqlalchemy/orm/strategies.py (working copy)
@@ -1474,20 +1474,24 @@
def _create_scalar_loader(self, context, key, _instance):
def load_scalar_from_joined_new_row(state, dict_, row):
# set a scalar object instance directly on the parent
# object, bypassing InstrumentedAttribute event handlers.
dict_[key] = _instance(row, None)
def load_scalar_from_joined_existing_row(state, dict_, row):
# call _instance on the row, even though the object has
# been created, so that we further descend into properties
existing = _instance(row, None)
+ if key in dict_:
+ assert dict_[key] is existing
+ else:
+ dict_[key] = existing
if existing is not None \
and key in dict_ \
and existing is not dict_[key]:
util.warn(
"Multiple rows returned with "
"uselist=False for eagerly-loaded attribute '%s' "
% self)
def load_scalar_from_joined_exec(state, dict_, row):
_instance(row, None)
- Dave
On 9/2/2015 7:29 PM, Dave Vitek wrote:
Hi all,
I've come across what I'm pretty sure is a bug with contains_eager
when there are multiple joinpaths leading to the same row, and only
some of those joinpaths are using contains_eager all they way down
the joinpath.
I've prepared a test case:
http://pastebin.com/CbyUMdqC
See the text at the top of the test case for further details.
- Dave
--
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.