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 sqlalchemy+...@googlegroups.com <javascript:> > > > <mailto:sqlalchemy+unsubscr...@googlegroups.com <javascript:> > <javascript:>>. > > > To post to this group, send email to sqlal...@googlegroups.com > > <javascript:> > > > <mailto:sqlal...@googlegroups.com <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 sqlalchemy+...@googlegroups.com <javascript:> > > <mailto:sqlalchemy+unsubscr...@googlegroups.com <javascript:>>. > > To post to this group, send email to sqlal...@googlegroups.com > <javascript:> > > <mailto:sqlal...@googlegroups.com <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 sqlalchemy+unsubscr...@googlegroups.com. To post to this group, send email to sqlalchemy@googlegroups.com. Visit this group at https://groups.google.com/group/sqlalchemy. For more options, visit https://groups.google.com/d/optout.