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 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> > <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 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 <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+unsubscr...@googlegroups.com <mailto:sqlalchemy+unsubscr...@googlegroups.com>. To post to this group, send email to sqlalchemy@googlegroups.com <mailto:sqlalchemy@googlegroups.com>. 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.