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.

Reply via email to