Where's my InstrumentedAttribute? Can't make a filtering relation.

893 views
Skip to first unread message

Nick Retallack

unread,
Oct 25, 2009, 3:31:15 PM10/25/09
to SQLElixir
This is a little fidgety, so bear with me.

So I define some elixir models with properties on them. This is
supposed to create InstrumentedAttributes as class methods, which I
can then use in queries like Movie.query.filter(Movie.title != None).
However, those attributes don't exist until after I explicitly run
setup_all(), which appears to be deprecated anyway. Observe:

>>> from elixir import *
>>> metadata.bind = "sqlite:///console.db"
>>> metadata.bind.echo = True
>>> class Movie(Entity):
... title = Field(Unicode)
...
>>> Movie.title
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'Movie' has no attribute 'title'
>>> setup_all()
/Users/nick/bin/env/pylons-0.9.7/lib/python2.6/site-packages/
Elixir-0.6.1-py2.6.egg/elixir/entity.py:412: SADeprecationWarning:
Session.mapper is deprecated. Please see
http://www.sqlalchemy.org/trac/wiki/UsageRecipes/SessionAwareMapper
for information on how to replicate its behavior.
>>> Movie.title
<sqlalchemy.orm.attributes.InstrumentedAttribute object at 0x1868ef0>


Now, the problem. I need that instrumented attribute if I'm going to
write a filter for a relationship. Observe:


>>> from elixir import *
>>> class Belonger(Entity):
... stuff = Field(Unicode)
... haver = ManyToOne('Haver')
...
>>> class Haver(Entity):
... all_belongings = OneToMany('Belonger')
... some_belongings = OneToMany('Belonger', filter='Belonger.stuff !
= None')
...
>>> setup_all()
/Users/nick/bin/env/pylons-0.9.7/lib/python2.6/site-packages/
Elixir-0.6.1-py2.6.egg/elixir/entity.py:412: SADeprecationWarning:
Session.mapper is deprecated. Please see
http://www.sqlalchemy.org/trac/wiki/UsageRecipes/SessionAwareMapper
for information on how to replicate its behavior.
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "build/bdist.macosx-10.3-i386/egg/elixir/__init__.py", line
145, in setup_all
File "build/bdist.macosx-10.3-i386/egg/elixir/entity.py", line 816,
in setup_entities
File "build/bdist.macosx-10.3-i386/egg/elixir/entity.py", line 421,
in setup_properties
File "build/bdist.macosx-10.3-i386/egg/elixir/entity.py", line 433,
in call_builders
File "build/bdist.macosx-10.3-i386/egg/elixir/relationships.py",
line 414, in create_properties
File "build/bdist.macosx-10.3-i386/egg/elixir/relationships.py",
line 722, in get_prop_kwargs
TypeError: 'str' object is not callable


Ok, so I can't use the string trick to define my filter expression.
And I can't refer to the InstrumentedAttribute before I call
setup_all. What am I supposed to do, call setup_all between every
model definition? Unfortunatly, I can't do that either, since the
reverse association then causes a KeyError since the class it's
related to isn't created yet. Observe:

from elixir import *
class Belonger(Entity):
stuff = Field(Unicode)
havers = ManyToOne('Haver')

setup_all() # line 6

class Haver(Entity):
all_belongers = OneToMany('Belonger')
some_belongers = OneToMany('Belonger', filter=Belonger.stuff !=
None)

Traceback (most recent call last):
File "test.py", line 6, in <module>
setup_all()
File "build/bdist.macosx-10.3-i386/egg/elixir/__init__.py", line
145, in setup_all
File "build/bdist.macosx-10.3-i386/egg/elixir/entity.py", line 816,
in setup_entities
File "build/bdist.macosx-10.3-i386/egg/elixir/entity.py", line 193,
in setup_relkeys
File "build/bdist.macosx-10.3-i386/egg/elixir/entity.py", line 433,
in call_builders
File "build/bdist.macosx-10.3-i386/egg/elixir/relationships.py",
line 402, in create_non_pk_cols
File "build/bdist.macosx-10.3-i386/egg/elixir/relationships.py",
line 569, in create_keys
File "build/bdist.macosx-10.3-i386/egg/elixir/relationships.py",
line 452, in target
File "build/bdist.macosx-10.3-i386/egg/elixir/__init__.py", line
106, in resolve
KeyError: 'Haver'

Gaetan de Menten

unread,
Oct 25, 2009, 4:13:31 PM10/25/09
to sqle...@googlegroups.com
On Sun, Oct 25, 2009 at 20:31, Nick Retallack <nickre...@gmail.com> wrote:

>>>> class Haver(Entity):
> ...   all_belongings = OneToMany('Belonger')
> ...   some_belongings = OneToMany('Belonger', filter='Belonger.stuff !
> = None')

use:

some_belongings = OneToMany('Belonger', filter=lambda c: c.stuff != None)

When you are stuck like this, it usually helps to look at the tests of
Elixir, which also serve as examples. I know that more visible
examples would be nicer, but I just don't have time for that. In your
case, see:

http://elixir.ematia.de/trac/browser/elixir/tags/0.7.0/tests/test_o2m.py#L127

--
Gaëtan de Menten
http://openhex.org

Nick Retallack

unread,
Oct 25, 2009, 5:54:06 PM10/25/09
to SQLElixir
Oh. Might want to fix the documentation then:

http://elixir.ematia.de/apidocs/elixir.relationships.html

--------------
OneToMany
...
filter
Specify a filter criterion (as a clause element) for this
relationship. This criterion will be and_'ed with the normal join
criterion (primaryjoin) generated by Elixir for the relationship. For
example: boston_addresses =

System Message: ERROR/3 (<string>, line 190)

Unexpected indentation.
OneToMany('Address', filter=Address.city == 'Boston')
---------------

Interesting. I expected those lambdas to act like post processing,
but on further inspection they do make it into the query properly.
Thanks.

On Oct 25, 1:13 pm, Gaetan de Menten <gdemen...@gmail.com> wrote:
> On Sun, Oct 25, 2009 at 20:31, Nick Retallack <nickretall...@gmail.com> wrote:
> >>>> class Haver(Entity):
> > ...   all_belongings = OneToMany('Belonger')
> > ...   some_belongings = OneToMany('Belonger', filter='Belonger.stuff !
> > = None')
>
> use:
>
> some_belongings = OneToMany('Belonger', filter=lambda c: c.stuff != None)
>
> When you are stuck like this, it usually helps to look at the tests of
> Elixir, which also serve as examples. I know that more visible
> examples would be nicer, but I just don't have time for that. In your
> case, see:
>
> http://elixir.ematia.de/trac/browser/elixir/tags/0.7.0/tests/test_o2m...
>
> --
> Gaëtan de Mentenhttp://openhex.org

Nick Retallack

unread,
Oct 25, 2009, 6:09:01 PM10/25/09
to SQLElixir
Something I noticed while testing this: the lambda doesn't appear to
use the InstrumentedAttributes at all -- it uses the real column names
instead. Thus, the example below will fail with an AttributeError
unless you change 'parent' to 'parent_id' in the lambda. Please
document this.

from elixir import *

class Belonger(Entity):
parent = ManyToOne('Belonger')
haver = ManyToOne('Haver')

class Haver(Entity):
all_belongers = OneToMany('Belonger')
root_belongers = OneToMany('Belonger', filter=lambda c: c.parent !=
None)

setup_all()


On Oct 25, 1:13 pm, Gaetan de Menten <gdemen...@gmail.com> wrote:
> On Sun, Oct 25, 2009 at 20:31, Nick Retallack <nickretall...@gmail.com> wrote:
> >>>> class Haver(Entity):
> > ...   all_belongings = OneToMany('Belonger')
> > ...   some_belongings = OneToMany('Belonger', filter='Belonger.stuff !
> > = None')
>
> use:
>
> some_belongings = OneToMany('Belonger', filter=lambda c: c.stuff != None)
>
> When you are stuck like this, it usually helps to look at the tests of
> Elixir, which also serve as examples. I know that more visible
> examples would be nicer, but I just don't have time for that. In your
> case, see:
>
Reply all
Reply to author
Forward
0 new messages