AttributeError: 'Session' object has no attribute 'mapper'

392 views
Skip to first unread message

renier

unread,
Sep 9, 2007, 12:14:31 PM9/9/07
to SQLElixir
I get the following trace with Elixir from trunk (as of 9/9/07 12:00pm
[-0400 GMT]), SqlAlchemy 0.4beta5, and TurboGears-1.0.3.2dev_r3487:

--

Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/usr/lib/python2.5/site-packages/Elixir-0.4.0-py2.5.egg/elixir/
entity.py", line 600, in __getattribute__
elixir.setup_all()
File "/usr/lib/python2.5/site-packages/Elixir-0.4.0-py2.5.egg/elixir/
__init__.py", line 120, in setup_all
setup_entities(_delayed_entities)
File "/usr/lib/python2.5/site-packages/Elixir-0.4.0-py2.5.egg/elixir/
__init__.py", line 107, in setup_entities
method()
File "/usr/lib/python2.5/site-packages/Elixir-0.4.0-py2.5.egg/elixir/
entity.py", line 365, in setup_mapper
*args, **kwargs)
File "/usr/lib/python2.5/site-packages/Elixir-0.4.0-py2.5.egg/elixir/
entity.py", line 673, in mapper
cls.mapper = self.context.mapper(cls, *args, **kwargs)
File "/usr/lib/python2.5/site-packages/SQLAlchemy-0.4.0beta5-
py2.5.egg/sqlalchemy/ext/activemapper.py", line 26, in __getattr__
return getattr(self.context.current, name)
AttributeError: 'Session' object has no attribute 'mapper'

--

To reproduce, I perform the following steps:
tg-admin shell
>>> import model # this model comes straight from Elixir example page
>>> dir(model.Visit.mapper)
**(Here I get traceback shown above)**
>>> dir(model.Visit)
['__class__', '__delattr__', '__dict__', '__doc__',
'__elixir_statements__', '__getattribute__', '__hash__', '__init__',
'__metaclass__', '__module__', '__new__', '__reduce__',
'__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__',
'_caller', '_class_key', '_descriptor', '_ready', '_table_key',
'get_by', 'lookup_visit', 'mapper', 'select', 'table']

Could one of the developers shed some light? I'm experimenting and
willing to test fixes and use the trunk.

Thanks,
--Renier

Gaetan de Menten

unread,
Sep 10, 2007, 5:27:04 AM9/10/07
to sqle...@googlegroups.com

This a variant of a known incompatibility issue between TurboGears and
the latest trunk version of Elixir. As, I explained in the following
thread, it would be cleaner to fix the problem on TurboGear's side of
things (by including explicit support for Elixir). I'll try to come up
with a decent solution to that problem in Elixir anyway.

http://groups.google.com/group/sqlelixir/browse_frm/thread/af8a646cf3c1bf8f/#

> Could one of the developers shed some light? I'm experimenting and
> willing to test fixes and use the trunk.

I'll let you know whenever I have a possible fix for this, so that you
can test it.
--
Gaëtan de Menten
http://openhex.org

renier

unread,
Sep 10, 2007, 4:56:01 PM9/10/07
to SQLElixir
What about the following patches:
- One patch to add a get method to Entity
- Another patch to make Elixir use scoped_session instead of
deprecated SessionContext
- Last patch to make TurboGears not use ActiveMapper at all because it
was initializing sqlalchemy.objectstore to its own objectstore. Made
it use Elixir instead.

Also, is there a ticket for adding Elixir support in TG that I can
refer to?

Appreciate your feedback,
--Renier

metaxa site-packages $ diff -u Elixir-0.4.0-py2.5.egg/elixir/
entity.py.orig Elixir-0.4.0-py2.5.egg/elixir/entity.py
--- Elixir-0.4.0-py2.5.egg/elixir/entity.py.orig 2007-09-10
15:19:43.000000000 -0400
+++ Elixir-0.4.0-py2.5.egg/elixir/entity.py 2007-09-10
16:11:42.000000000 -0400
@@ -641,6 +641,18 @@
return cls.query().filter_by(*args, **kwargs).first()
get_by = classmethod(get_by)

+ def get(cls, id):
+ for field in cls._descriptor.fields.values():
+ if field.primary_key:
+ idname = field.colname
+ break
+ else:
+ idname = 'id'
+
+ idarg = { idname: id }
+ return cls.query().filter_by(**idarg).first()
+ get = classmethod(get)
+
# DEPRECATED LAND
def select(cls, *args, **kwargs):
warnings.warn("The select method on the class is deprecated."
metaxa site-packages $ diff -u Elixir-0.4.0-py2.5.egg/elixir/
__init__.py.orig Elixir-0.4.0-py2.5.egg/elixir/__init__.py
--- Elixir-0.4.0-py2.5.egg/elixir/__init__.py.orig 2007-09-10
13:39:10.000000000 -0400
+++ Elixir-0.4.0-py2.5.egg/elixir/__init__.py 2007-09-10
13:40:26.000000000 -0400
@@ -18,7 +18,7 @@

import sqlalchemy

-from sqlalchemy.ext.sessioncontext import SessionContext
+from sqlalchemy.orm import scoped_session
from sqlalchemy.types import *

from elixir.options import using_options, using_table_options, \
@@ -58,7 +58,7 @@
# this only happens when the threadlocal extension is used
objectstore = sqlalchemy.objectstore
except AttributeError:
- objectstore =
Objectstore(SessionContext(sqlalchemy.orm.create_session))
+ objectstore =
Objectstore(scoped_session(sqlalchemy.orm.create_session))

metadatas = set()

metaxa site-packages $ diff -u TurboGears-1.0.3.2dev_r3487-py2.5.egg/
turbogears/database.py.orig TurboGears-1.0.3.2dev_r3487-py2.5.egg/
turbogears/database.py
--- TurboGears-1.0.3.2dev_r3487-py2.5.egg/turbogears/
database.py.orig 2007-09-10 16:43:39.000000000 -0400
+++ TurboGears-1.0.3.2dev_r3487-py2.5.egg/turbogears/
database.py 2007-09-10 16:45:37.000000000 -0400
@@ -27,7 +27,7 @@
# Provide support for sqlalchemy
try:
import sqlalchemy
- from sqlalchemy.ext import activemapper, sessioncontext
+ import elixir
from sqlalchemy.exceptions import InvalidRequestError

def get_engine():
@@ -58,9 +58,8 @@

return sqlalchemy.orm.create_session()

- metadata = activemapper.metadata
- session = activemapper.Objectstore(create_session)
- activemapper.objectstore = session
+ metadata = elixir.metadata
+ session = elixir.objectstore

def bind_meta_data():
get_engine()

renier

unread,
Sep 12, 2007, 1:31:49 PM9/12/07
to SQLElixir
Any comments on this?

I also have to add another necessary change to TG's database.py,
because of the change to using scoped_session:
@@ -384,7 +383,7 @@
req = cherrypy.request
if hasattr(req, 'sa_transaction') and
req.sa_transaction.session.transaction:
req.sa_transaction.rollback()
- del session.context.current
+ session.context.registry.clear()
req.sa_transaction = session.create_transaction()

--Renier

renier

unread,
Sep 13, 2007, 1:15:06 PM9/13/07
to SQLElixir

Gaetan de Menten

unread,
Sep 21, 2007, 12:59:42 PM9/21/07
to sqle...@googlegroups.com
As I said already, sorry for the slow answer to all your posts...

On 9/10/07, renier <ren...@morales-rodriguez.net> wrote:

> metaxa site-packages $ diff -u Elixir-0.4.0-py2.5.egg/elixir/
> entity.py.orig Elixir-0.4.0-py2.5.egg/elixir/entity.py
> --- Elixir-0.4.0-py2.5.egg/elixir/entity.py.orig 2007-09-10
> 15:19:43.000000000 -0400
> +++ Elixir-0.4.0-py2.5.egg/elixir/entity.py 2007-09-10
> 16:11:42.000000000 -0400
> @@ -641,6 +641,18 @@
> return cls.query().filter_by(*args, **kwargs).first()
> get_by = classmethod(get_by)
>
> + def get(cls, id):
> + for field in cls._descriptor.fields.values():
> + if field.primary_key:
> + idname = field.colname
> + break
> + else:
> + idname = 'id'
> +
> + idarg = { idname: id }
> + return cls.query().filter_by(**idarg).first()
> + get = classmethod(get)

You're correct to think we need to re-add a get method, though there
is a simpler way to do so:

def get(cls, *args, **kwargs):
return cls._descriptor.objectstore.session.get(*args, **kwargs)
get = classmethod(get)

This is now commited to trunk.

> +++ Elixir-0.4.0-py2.5.egg/elixir/__init__.py 2007-09-10
> 13:40:26.000000000 -0400
> @@ -18,7 +18,7 @@
>
> import sqlalchemy
>
> -from sqlalchemy.ext.sessioncontext import SessionContext
> +from sqlalchemy.orm import scoped_session
> from sqlalchemy.types import *
>
> from elixir.options import using_options, using_table_options, \
> @@ -58,7 +58,7 @@
> # this only happens when the threadlocal extension is used
> objectstore = sqlalchemy.objectstore
> except AttributeError:
> - objectstore =
> Objectstore(SessionContext(sqlalchemy.orm.create_session))
> + objectstore =
> Objectstore(scoped_session(sqlalchemy.orm.create_session))
>
> metadatas = set()

This would be fine for people using SA 0.4 but would break on SA
0.3.10 and earlier. The goal is to support both SA 0.4 and SA 0.3.10
at the same time.

> --- TurboGears-1.0.3.2dev_r3487-py2.5.egg/turbogears/
> database.py.orig 2007-09-10 16:43:39.000000000 -0400
> +++ TurboGears-1.0.3.2dev_r3487-py2.5.egg/turbogears/
> database.py 2007-09-10 16:45:37.000000000 -0400
> @@ -27,7 +27,7 @@
> # Provide support for sqlalchemy
> try:
> import sqlalchemy
> - from sqlalchemy.ext import activemapper, sessioncontext
> + import elixir
> from sqlalchemy.exceptions import InvalidRequestError
>
> def get_engine():
> @@ -58,9 +58,8 @@
>
> return sqlalchemy.orm.create_session()
>
> - metadata = activemapper.metadata
> - session = activemapper.Objectstore(create_session)
> - activemapper.objectstore = session
> + metadata = elixir.metadata
> + session = elixir.objectstore
>
> def bind_meta_data():
> get_engine()

I guess you can't just replace ActiveMapper with Elixir, as people
still using ActiveMapper would complain. If you go down that route
anyway, you could simplify the database.py further as with the latest
versions of Elixir (>= 0.3), you don't need to connect the metadata in
each thread.

Btw: thanks for your efforts in trying to fix those issues. I really
appreciate it.

renier

unread,
Sep 21, 2007, 3:18:54 PM9/21/07
to SQLElixir

Great, thanks.

>
>
>
> > +++ Elixir-0.4.0-py2.5.egg/elixir/__init__.py 2007-09-10
> > 13:40:26.000000000 -0400
> > @@ -18,7 +18,7 @@
>
> > import sqlalchemy
>
> > -from sqlalchemy.ext.sessioncontext import SessionContext
> > +from sqlalchemy.orm import scoped_session
> > from sqlalchemy.types import *
>
> > from elixir.options import using_options, using_table_options, \
> > @@ -58,7 +58,7 @@
> > # this only happens when the threadlocal extension is used
> > objectstore = sqlalchemy.objectstore
> > except AttributeError:
> > - objectstore =
> > Objectstore(SessionContext(sqlalchemy.orm.create_session))
> > + objectstore =
> > Objectstore(scoped_session(sqlalchemy.orm.create_session))
>
> > metadatas = set()
>
> This would be fine for people using SA 0.4 but would break on SA
> 0.3.10 and earlier. The goal is to support both SA 0.4 and SA 0.3.10
> at the same time.

What about deciding to use SessionContext vs. scoped_session based on
sqlalchemy.__version__, when importing elixir? That way it would work
for both SA 0.3 and 0.4.

Do you mean that I don't need to set metadata or session in TG's
database.py?

I think, whether TG internally uses activemapper or Elixir to wrap
around sqlalchemy shouldn't concern the end user. The problem with
database.py importing activemapper is that it will create an
objectstore without wrapping SessionContext or ScopedSession around
create_session. I'm not sure if this is significant. You tell me.

I'll try to continue pushing for this particular database.py change on
the TG side.

--Renier

Gaetan de Menten

unread,
Sep 24, 2007, 4:30:30 AM9/24/07
to sqle...@googlegroups.com
On 9/21/07, Gaetan de Menten <gdem...@gmail.com> wrote:

> > + def get(cls, id):
> > + for field in cls._descriptor.fields.values():
> > + if field.primary_key:
> > + idname = field.colname
> > + break
> > + else:
> > + idname = 'id'
> > +
> > + idarg = { idname: id }
> > + return cls.query().filter_by(**idarg).first()
> > + get = classmethod(get)
>
> You're correct to think we need to re-add a get method, though there
> is a simpler way to do so:
>
> def get(cls, *args, **kwargs):
> return cls._descriptor.objectstore.session.get(*args, **kwargs)
> get = classmethod(get)
>
> This is now commited to trunk.


Hmmm. This should have read:

def get(cls, *args, **kwargs):
return cls.query().get(*args, **kwargs)
get = classmethod(get)

I guess I wasn't fully awake when I wrote that. Sorry about that. This
is now properly fixed in trunk.

Gaetan de Menten

unread,
Sep 24, 2007, 10:02:50 AM9/24/07
to sqle...@googlegroups.com

No. I mean, that you could probably (I haven't actually tried it) use
a vanilla create_session in TG's database.py instead of the one they
provide because, as far as I can tell, they had to provide one for the
sole purpose of reconnecting the metadata to the engine in each
thread. And this could now be done globally as simply metadata.bind =
...

> I think, whether TG internally uses activemapper or Elixir to wrap
> around sqlalchemy shouldn't concern the end user. The problem with
> database.py importing activemapper is that it will create an
> objectstore without wrapping SessionContext or ScopedSession around
> create_session. I'm not sure if this is significant. You tell me.

The only problem with that is that there was an isinstance(...,
Objectstore) check somewhere which failed because it really meant:
isinstance(..., Objectstore) and didn't work with the activemapper's
Objectstore wrapper. Now I've made the test more liberal and it should
accept ActiveMapper's objectstore. So it means that TurboGears should
now work again with the latest version of Elixir, even with no fix on
their side. But please, PLEASE, try to get them to fix the issue
anyway (ie provide a proper Elixir integration), because it'll bomb
again if we ever change the objectstore definition again.

Reply all
Reply to author
Forward
0 new messages