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:>>.
> 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.