On 08/08/2016 07:43 AM, Florian Rüchel wrote:
I created a gist that recreates the issue when running SQLAlchemy
1.1.0b3: https://gist.github.com/Javex/41c58b098c1e5736cb2b21c4b6708be3
great test. So this is to do with the lazy loader which is not
expecting to see what it's seeing here. because your bound parameter
is part of a simple primary key comparison, it's getting sucked into the
lazyload SQL generation mechanics and multiple things are going wrong
with it from that point.
I've summarized the issues here:
https://bitbucket.org/zzzeek/sqlalchemy/issues/3767/primaryjoin-w-user-defined-bind-parameter
unfortunately I don't know that there's much of a workaround now except
perhaps to make a special subclass of BindParameter that tries to hide
here, but that might require a lot of hacky stuff to get it to "hide" at
the right time and not when it actually needs to be used.
Traceback (most recent call last):
File "broken_autojoin.py", line 73, in <module>
print('Title: %s' %article.german.title)
File
"/home/javex/.virtualenvs/mtc3/lib/python3.5/site-packages/sqlalchemy/orm/attributes.py",
line 237, in __get__
return self.impl.get(instance_state(instance), dict_)
File
"/home/javex/.virtualenvs/mtc3/lib/python3.5/site-packages/sqlalchemy/orm/attributes.py",
line 584, in get
value = self.callable_(state, passive)
File
"/home/javex/.virtualenvs/mtc3/lib/python3.5/site-packages/sqlalchemy/orm/strategies.py",
line 553, in _load_for_state
passive
File
"/home/javex/.virtualenvs/mtc3/lib/python3.5/site-packages/sqlalchemy/orm/strategies.py",
line 589, in _get_ident_for_use_get
for pk in self.mapper.primary_key
File
"/home/javex/.virtualenvs/mtc3/lib/python3.5/site-packages/sqlalchemy/orm/strategies.py",
line 589, in <listcomp>
for pk in self.mapper.primary_key
KeyError: Column('language_id', Integer(), ForeignKey('language.id'),
table=<article_i18n>, primary_key=True, nullable=False)
Let me know if this breaks for you as well. If this is in fact a bug
(and not some stupid mistake I made) and you happen to create an issue /
fix for this I'd also be interested in seeing how it was fixed and where
the problem was, so please link it here if possible so I can follow it :)
Cheers,
Florian
On Sunday, 7 August 2016 23:58:04 UTC+10, Mike Bayer wrote:
On 08/06/2016 08:03 PM, Florian Rüchel wrote:
> Following up on this. I have implemented it in my application and it
> works beautifully when querying for the object.
>
> However, while writing tests, I discovered that if the object is
> expired, it doesn't know how to refresh it. To explain the issue,
I need
> to expand my original (simplified) example. The MyObjI18N class
doesn't
> store the locale directly. Instead it has another foreignkey to a
> Language table. So my query in the above case looks actually like
this:
>
> and_(MyObj.id == MyObjI18N.obj_id, request.language_id ==
Language.id)
>
> Now the problem is this: It doesn't seem to remember this language_id
> when refreshing, leading to the following exception:
>
> .../python3.5/site-packages/sqlalchemy/orm/attributes.py:237: in
__get__
> return self.impl.get(instance_state(instance), dict_)
> .../python3.5/site-packages/sqlalchemy/orm/attributes.py:584: in get
> value = self.callable_(state, passive)
> .../python3.5/site-packages/sqlalchemy/orm/strategies.py:553: in
> _load_for_state
> passive
> .../python3.5/site-packages/sqlalchemy/orm/strategies.py:589: in
> _get_ident_for_use_get
> for pk in self.mapper.primary_key
> _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
_ _ _
> _ _ _
>
> .0 = <tuple_iterator object at 0x7fe545dc97b8>
>
> get_attr(
> state,
> dict_,
> self._equated_columns[pk],
> passive=passive)
>> for pk in self.mapper.primary_key
> ]
> E KeyError: Column('language_id', Integer(),
> ForeignKey('language.id <http://language.id>'),
table=<challenge_i18n>, primary_key=True,
> nullable=False)
>
> .../python3.5/site-packages/sqlalchemy/orm/strategies.py:589:
KeyError
>
> I dug into the problem a bit but this goes way more into the core of
> SQLAlchemy than I am able to understand. In the end it seemed to
be that
> on the join condition the lazy clause created didn't contain the
equated
> column for language_id. But here my understanding of the working
> internals comes to an end.
>
> I would have assumed that the instance somehow 'remembered' that
column
> from when it was loaded and would refresh it in the same way as it
was
> loaded. And from the setup, I feel like it should be possible. My
best
> guess would be that the primaryjoin I return doesn't help it
> 'understand' that it has that link to a language_id column.
I can't see how that would happen unless we're talking about something
like clear_mappers(). the construction of SQL expressions and their
linkages to mappers do not change with "expiration", and the recipe
uses
a bindparameter() object that similarly doesn't change.
I can offer no insight here without complete specifics.
>
> FWIW I am running SQLAlchemy 1.1.0b3, in case this is actually
> unexpected behaviour.
>
> Cheers,
> Florian
>
> On Friday, 5 August 2016 00:24:30 UTC+10, Mike Bayer wrote:
>
>
>
> On 08/04/2016 10:14 AM, Florian Rüchel wrote:
> > I have a relationship that depends on a query time variable to
> determine
> > the correct join. The use case is request-time localization
in a web
> > application. When running the query during a request, I want to
> > determine the locale and only load the translation for the
current
> > language for a given object. However, the primaryjoin condition
> callable
> > is evaluated at mapping time instead which only happens once
> instead of
> > on every request.
> >
> > Here is a quick sample:
> >
> > def get_myobj_primaryjoin():
> > return and_(MyObj.id == MyObjI18N.obj_id, request.locale ==
> > MyObjI18N.lang)
> >
> >
> > class MyObj(Base):
> > id = Column(Integer, primary_key=True)
> > _current_translation = relationship(MyObjI18N,
uselist=False,
> > primaryjoin=get_myobj_primaryjoin, lazy='joined')
> >
> >
> > class MyObjI18N(Base):
> > obj_id = Column(ForeignKey(MyObj.id), primary_key=True)
> > lang = Column(String)
> >
> > This should give a rough idea of the issue: request.locale
changes at
> > query time, that is, if I do MyObj.query in two different
> requests, it
> > won't work, it will always take the first time it was called.
> >
> > Note that I was previously using a with_transformation
approach when
> > building the query but I wanted to remove the necessity to
add that
> > every time a build a query and would have it much rather built
> implicitly.
> >
> > Any ideas are highly appreciated, no argument I can pass to
> > "relationship" seems to help my use case.
>
>
> we use a bound parameter for this and a recipe for getting a
value in
> there can be seen at
>
https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/GlobalFilter
<https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/GlobalFilter>
>
<https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/GlobalFilter
<https://bitbucket.org/zzzeek/sqlalchemy/wiki/UsageRecipes/GlobalFilter>>
> .
> In particular the lazyload case can only be affected using
a custom
> MapperOption as described near the bottom of that recipe.
>
>
>
> >
> > --
> > 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] <javascript:>
> > <mailto:[email protected]
<javascript:> <javascript:>>.
> > To post to this group, send email to [email protected]
> <javascript:>
> > <mailto:[email protected] <javascript:>>.
> > Visit this group at
https://groups.google.com/group/sqlalchemy
<https://groups.google.com/group/sqlalchemy>
> <https://groups.google.com/group/sqlalchemy
<https://groups.google.com/group/sqlalchemy>>.
> > For more options, visit https://groups.google.com/d/optout
<https://groups.google.com/d/optout>
> <https://groups.google.com/d/optout
<https://groups.google.com/d/optout>>.
>
> --
> 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] <javascript:>
> <mailto:[email protected] <javascript:>>.
> To post to this group, send email to [email protected]
<javascript:>
> <mailto:[email protected] <javascript:>>.
> Visit this group at https://groups.google.com/group/sqlalchemy
<https://groups.google.com/group/sqlalchemy>.
> For more options, visit https://groups.google.com/d/optout
<https://groups.google.com/d/optout>.
--
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]
<mailto:[email protected]>.
To post to this group, send email to [email protected]
<mailto:[email protected]>.
Visit this group at https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.
--
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 https://groups.google.com/group/sqlalchemy.
For more options, visit https://groups.google.com/d/optout.