I created a gist that recreates the issue when running SQLAlchemy
1.1.0b3: https://gist.github.com/Javex/41c58b098c1e5736cb2b21c4b6708be3
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'), 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>
> > .
> > 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>.
> > > 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] <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.
> > 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.