mapping of tables to objects in pylons controller?

0 views
Skip to first unread message

gjhi...@googlemail.com

unread,
Feb 19, 2007, 2:36:19 AM2/19/07
to SQLElixir
I'm enjoying using Elixir in Pylons. I transferred my models over from
TurboGears, (was even able to re-use the existing data in the database
tables). Cool.

How does one get object to table mapping in a Pylons controller? I can
access the db via a SessionContext.query imported from either
pylons.database or sqlalchemy but the class mappers don't seem to be
connected to an engine:

Could not locate any Engine bound to mapper 'Mapper|Page|page'

however, "session_context.query(MyClass).select()" returns the
expected results from the db.

Where am I going wrong?

Gaetan de Menten

unread,
Feb 19, 2007, 3:27:11 AM2/19/07
to sqle...@googlegroups.com
Well, I don't know anything about Pylons, so far so I can't help you
right away. If you could provide a minimal (but complete) test-case
along with the full traceback I'd probably be able to tell you more,
and possibly debug Elixir, if it needs to be.

Of course, if anybody else with Pylons knowledge want to tackle the
problem, feel free to do it ;-). I'd be grateful about it.

-Gaëtan.

Graham Higgins

unread,
Feb 19, 2007, 11:52:29 AM2/19/07
to sqle...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 19 Feb 2007, at 08:27, Gaetan de Menten wrote:

> Well, I don't know anything about Pylons, so far so I can't help you
> right away.

Thank you Gaetan for your kind offer and a prompt response.

I'm happy to report that I managed to arrive at a solution and now
have Elixir successfully running within Pylons.

> If you could provide a minimal (but complete) test-case

Thank you also for reminding me that the tests might well contain the
answer.

>> How does one get object to table mapping in a Pylons controller?

By ensuring that the engine is properly bound :-)

Cheers,

Graham Higgins.

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (Darwin)

iQCVAgUBRdnVzlnrWVZ7aXD1AQL+kQP/Rx19JB3OaEA/gHI49J8ponFH9zqygSfx
ibrkwAVg4uhLLOay3HXqtr/hZxfXj83Tvi/h7wIT7LWDPlKjfzPBuFnGiNxGiTIq
LFOeOHPsdRzmXB4G0/n6I83vLpqgogqHDXX1+Sq9tgVlMjxmZ0ble9yshEaKfvRV
TDOMtyewkZc=
=u21v
-----END PGP SIGNATURE-----

__wyatt

unread,
Feb 21, 2007, 8:55:54 AM2/21/07
to SQLElixir
On Feb 19, 8:52 am, Graham Higgins <gjhigg...@googlemail.com> wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> On 19 Feb 2007, at 08:27, Gaetan de Menten wrote:
>
> > Well, I don't know anything about Pylons, so far so I can't help you
> > right away.
>
> Thank you Gaetan for your kind offer and a prompt response.
>
> I'm happy to report that I managed to arrive at a solution and now
> have Elixir successfully running within Pylons.
>
> > If you could provide a minimal (but complete) test-case
>
> Thank you also for reminding me that the tests might well contain the
> answer.
>
> >> How does one get object to table mapping in a Pylons controller?
>
> By ensuring that the engine is properly bound :-)

I'm having a similar problem. My solution for now is to call
elixir.metadata.connect(self.dburi) in my controller, but that doesn't
seem right since I should only need to connect once (when the
application starts up). I tried doing something like in the test
cases:

engine = create_engine(self.dburi)
elixir.metadata.connect(engine)

but that didn't help.

How do you make sure the engine is properly bound? I'm guessing you
have to import sqlalchemy and do something with that. Would it be
possible for elixir to wrap this up so sqlalchemy can remain behind
the scenes?

Thanks,

__wyatt

Graham Higgins

unread,
Feb 21, 2007, 10:34:02 AM2/21/07
to sqle...@googlegroups.com
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1


On 21 Feb 2007, at 13:55, __wyatt wrote:

> On Feb 19, 8:52 am, Graham Higgins <gjhigg...@googlemail.com> wrote:
>>>> How does one get object to table mapping in a Pylons controller?
>>
>> By ensuring that the engine is properly bound :-)
>
> I'm having a similar problem. My solution for now is to call
> elixir.metadata.connect(self.dburi) in my controller,

That's more or less where I ended up. If it works for you, I'd say
stick with it for the time being.

The context for me is porting an existing TG project (which uses
ActiveMapper and Genshi) over to Pylons using Elixir and Mako and
trying to be as lazy as possible about the whole thing.

I took my cue from the AuthKit code [1]:

I created a database.py in the lib subdirectory. Contents are:

=====================================
from elixir import *
from elixir import metadata, objectstore
import elixir
import sqlalchemy
from paste.deploy.converters import asbool

engine = None

def connect_engine():
"Retrieves the engine based on the current configuration"
global engine
if not engine:
from paste.deploy import CONFIG
config = CONFIG['app_conf']
dburi = config.get("sqlalchemy.dburi")
if not dburi:
raise KeyError("No sqlalchemy database config found!")
echo = asbool(config.get("sqlalchemy.echo", False))
engine = sqlalchemy.create_engine(dburi, echo=echo)
metadata.connect(engine)
elif not metadata.is_bound():
metadata.connect(engine)
return engine

connect_engine()
========================================

and I import that in base.py:

from <appname>.lib import database

Yeah, yeah, I know but the "activemapper" engine binding thing was a
real PITA and I needed to circumvent it in order to continue making
progress in other areas, such migrating from Genshi to Mako, so as
soon as I had something that worked, I moved on.

> but that doesn't
> seem right since I should only need to connect once (when the
> application starts up).

That's how it appeared to me, too.

A Google search on the "engine not bound" errmsg produces an elderly
posting from Michael Bayer mentioning "out of scope" (w.r.t
threading, I think) and the relevant Pylons doc [2] seems to track a
change away from threadlocal and objectstore in favour of
session_context --- but the doc is annotated as possibly incorrect
w.r.t Pylons 0.9.4 and was originally written in Oct 2006.

Poking around in the debugger reveals a maze of twisty little object
bindings and simply showed me that I currently don't know enough
about Pylons behind-the-scenes to produce anything better, so I was
rather hoping that if I volunteered to document how to use Elixir
with Pylons, I could get an assist from someone experienced with the
Pylons back-end and possibly get a proper resolution of the issue ;-)

What was doing my crunch was that "session_context.query(Page).select
()" was working fine and demonstrated that somewhere an engine *was*
properly bound -- but I didn't want to have to go through all my
controllers recoding all the ORM calls to use the session_context
access scheme.

Other than this issue, my migration of my object model from TG SA/
ActiveMapper was seamless --- Pylons and TG are currently both
reading from the same Postgres tables, no RDBMS changes were required
at all.

> How do you make sure the engine is properly bound? I'm guessing you
> have to import sqlalchemy and do something with that. Would it be
> possible for elixir to wrap this up so sqlalchemy can remain behind
> the scenes?

Apologies for being oblique about "ensuring that the engine is
properly bound", I'm pretty sure that's more or less where the answer
lies but, like you, I'm a little suspicious that the answer I
produced for myself causes superfluous reconnecting, I guess a full
RDBMS trace would reveal the truth of that.

Perhaps the approach is merely inelegant instead of plain wrong,
dunno 'till I grok Pylons' internal workings better :-(

Cheers,

Graham.

[1] http://authkit.org/trac/browser/AuthKit/trunk/examples/pylons/
AuthDemo/authdemo/lib/database.py?rev=46
[2] http://pylonshq.com/project/pylonshq/wiki/SqlAlchemyWithPylons

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.5 (Darwin)

iQCVAgUBRdxma1nrWVZ7aXD1AQIYNAP/aSzLnVNK7Au9e7xL+W/J0WN7Zs+NdB0L
T4GQtYkRI9FufGiAxGYCbHEMP9xGsYj1ukZOMS/cO1uxyLmXA035HvZsZtDByzlg
gT4ztWAKkJMhK7IsB+U+q7RdoYTYPUNrEGvYcevbroTHhtwdtthmOOfl/B6d4atl
X27AWyCk5Qc=
=Iz3/
-----END PGP SIGNATURE-----

__wyatt

unread,
Feb 21, 2007, 10:12:35 PM2/21/07
to SQLElixir
OK, I think I get it now:

elixir.metadata is a SQLAlchemy DynamicMetaData object[1], which means
it's not always bound to a particular engine. The call to
metadata.connect() associates elixir.metadata with an engine, but it
doesn't reconnect to the database (unless you call it with a DSN?).

The connect call needs to happen in the thread where you're trying to
use your elixir entities. In Pylons, this means in your controller.
Right now, I'm doing this in a controller subclass, but I think the
thing to do is to add the metadata.connect call to the __call__
method in lib.base.BaseController.

You probably want to create only one engine in your app, so you'd do
something similar to Graham's AuthKit example, setting up a global
engine somewhere that's accessible via your model, and you'd end up
with:

import elixir
import myproject.lib import models as model

class BaseController(WSGIController):
def __call__(self, environ, start_response):
# Insert any code to be run per request here. The Routes
match
# is under environ['pylons.routes_dict'] should you want to
check
# the action or route vars here

elixir.metadata.connect(model.my_global_engine)
# or delegate to model.connect() that does this, so
your controllers don't need to know about elixir

return WSGIController.__call__(self, environ,
start_response)


This seems reasonable to me (if I've really grokked everything) and
DRY.

__wyatt

[1] http://www.sqlalchemy.org/docs/metadata.myt#metadata_tables_binding

Reply all
Reply to author
Forward
0 new messages