Pylons without SAContext

16 views
Skip to first unread message

Mike Orr

unread,
Aug 13, 2007, 11:05:53 PM8/13/07
to pylons...@googlegroups.com
I looked over Ben's and Mike's proposals. It's actually close to what
I suggested to Mike a week ago, that maybe SAContext is no longer
relevant in a SQLAlchemy 0.4 era. He replied that it's good to have a
tidy container for engines/metadatas/sessions. But we've seen that
SAContext's opaqueness is both a blessing and a curse. If Ben and
Mike both endorse this application pattern, I'm inclined to just
modify my application that way, and then I'll be less interested in
supporting SAContext. SAContext was necessary to wean people off
pylons.database and session_context.current.bind_to, and to provide a
way to parse engine args. But now maybe it's no longer necessary.

MikeB:
> i put a rudimental "starter" engine_from_config() in
sqlalchemy.engine, available in the latest trunk....it will come up if
you "pydoc sqlalchemy.engine". Eventually we'll expand it out to
work for dialect specific arguments too (we had planned on that
anyway).

Excellent! This really belongs in SQLAlchemy.

BTW, it's sqlalchemy.engine_from_config in an application.

I would split it into a get_engine_config so that people can provide
either default arguments and/or overriding arguments. It's not too
much to do:
create_engine(**engine_from_config(config, 'sqlalchemy.engine1.'))
And it would allow people to do:
args = {...default args...}
args.update(engine_from_config(config))
args.update(...overriding args...)
create_engine(args)

Just make sure the error message is meaningful if a required key is
missing or an int is malformed. Something that suggests the original
keyname, if not the config file too.
(The config filename could be passed in as an optional string, as is
common in other programs.)

On 8/13/07, Ben Bangert <b...@groovie.org> wrote:
> On Aug 13, 2007, at 4:41 PM, Mike Orr wrote:
>
> > Has there been a decision whether to put app_scope in the Pylons core?
> > I'm going to make a new version of SAContext soon. It would be
> > cleaner (arguably) to import app_scope rather than doing something
> > lower level, don't you think? Because Pylons knows better than
> > anybody what the sharing boundaries for an application are. This
> > would also allow me to reduce PylonsSAContext to a single function,
> > get_pylons_engine_config().
>
> If the user setup the engine in environment.py, and stashed it in
> config, it'd automatically be in appscope to retrieve it from
> pylons.config (which will always refer to the right one).

This is an argument to scoped_session; it can be a string or a
function. I'm not sure if a PylonsConfig instance would be allowed.
Anyway, it has to ge the the intersection of the current thread and
current application. So:

def app_scope():
return "Pylons:%s:%s" % (thread.id, id(pylons.config))

The user could inline this with a lambda, but that's ugly.

> environment.py:
> config['pylons.g'] = app_globals.Globals()
> config['pylons.h'] = janice.lib.helpers
> config['pylons.g'].sa_engine = engine_from_config(config)

pylons.g would be preferred over config['pylons.g'] if possible.

We'd have to show an example of multiple engines.

> model.py:
> myothertable = Table('someothertable', metadata,
> autoload=True,autoload_with=pylons.config['pylons.g'].sa_engine)

Ditto about pylons.g. Is that available only during a request?

> And in base.py:
>
> # add a "remove" call at the end of each request.
> class BaseController(WSGIController):
> def __call__(self, environ, start_response):
> try:
> # Bind the thread-local for this session to this app
> scope's engine
> Session.configure(bind=g.sa_engine)
> return WSGIController.__call__(self, environ,
> start_response)
> finally:
> model.Session.remove()

The session has to be imported from the model if there are model
functions or methods.

MikeB wrote:
> class MyController(BaseController):
def do_something(self):
x = SomeObject()
y = SomeObject()
z = Session.query(ZClass).filter(...).one()

Session.save(x)
Session.save(y)

Session.commit()

return render_response(...)

I would put most of do_something in the model.

What else does the scope_func.mapper do besides automatic save and the
.query property? assignmapper had a session property for instance.
Am I guaranteed that .query is the only method added to my classes?

--
Mike Orr <slugg...@gmail.com>

Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted
Message has been deleted

Michael Bayer

unread,
Aug 14, 2007, 9:38:44 PM8/14/07
to pylons-devel

On Aug 13, 11:05 pm, "Mike Orr" <sluggos...@gmail.com> wrote:

> MikeB wrote:
> > class MyController(BaseController):
>
> def do_something(self):
> x = SomeObject()
> y = SomeObject()
> z = Session.query(ZClass).filter(...).one()
>
> Session.save(x)
> Session.save(y)
>
> Session.commit()
>
> return render_response(...)
>
> I would put most of do_something in the model.

OK well, the idea is that the calls to those things still originates
from within the controller so that was the idea of illustrating that.

>
> What else does the scope_func.mapper do besides automatic save and the
> .query property? assignmapper had a session property for instance.
> Am I guaranteed that .query is the only method added to my classes?
>

(looks at the code) currently its just "query", yes. I think all the
rest of the stuff you should be talking to a Session. as far as a
"guarantee", if the userbase complains like mad and makes good cases
for more methods, that could change, but its unlikely. but also, the
behavior of the "monkeypatch method" thing was such that it doesn't
overwrite existing methods.


So id like for us to get our story straight, take Ben's pattern and
clean it up, make a nice wiki document out of it, and put it to
press. if we tell a consistent story, then people will adjust to it.
i agree that the "magic" of SAContext, even though it was only meant
as an "x" with which to place "y", "z" and "q", seemed to confuse
people. then again we dont know how many people just use pylons and
SA and arent really confused since they arent posting questions.

i just want the story of "how to integrate" to be consistent and laid
out very clearly, whether it be through documentation alone or helper
objects. im hoping Session's contextual abilities will make it
easier.


Mike Orr

unread,
Aug 14, 2007, 11:53:30 PM8/14/07
to pylons...@googlegroups.com
On 8/14/07, Michael Bayer <zzz...@gmail.com> wrote:
> So id like for us to get our story straight, take Ben's pattern and
> clean it up, make a nice wiki document out of it, and put it to
> press.

Sounds good to me. The Pylons Cookbook is where people will expect to
find it. Once it's done I'll deprecate SAContext.

--
Mike Orr <slugg...@gmail.com>

James Gardner

unread,
Aug 15, 2007, 6:45:36 AM8/15/07
to pylons...@googlegroups.com
Hi all,

Can I just add that if this is going to be the recommended approach it
might be better to update the QuickWiki tutorial and its associated
example code to use it rather than creating a new wiki article? That way
it forms a more official part of the Pylons docs.

Cheers,

James

Mike Orr

unread,
Aug 15, 2007, 11:04:53 AM8/15/07
to pylons...@googlegroups.com
On 8/15/07, James Gardner <ja...@pythonweb.org> wrote:
> Can I just add that if this is going to be the recommended approach it
> might be better to update the QuickWiki tutorial and its associated
> example code to use it rather than creating a new wiki article? That way
> it forms a more official part of the Pylons docs.

I think both are needed. QuickWiki needs to be updated for Pylons
0.9.6 anyway, but that's a larger project. Database usage is such a
common and important task that it deserves its own article, not just
shoved inside an application tutorial.

A chapter in the Pylons official docs (hint) would give it greater
prominence than a Cookbook article, but I'm not in control of that.
Part of the Cookbook's charter is that the best of it will be
periodically migrated into the official docs.

--
Mike Orr <slugg...@gmail.com>

Alexandre CONRAD

unread,
Aug 15, 2007, 12:56:39 PM8/15/07
to pylons...@googlegroups.com
Hi,

Mike Orr wrote:
> I looked over Ben's and Mike's proposals. It's actually close to what
> I suggested to Mike a week ago, that maybe SAContext is no longer
> relevant in a SQLAlchemy 0.4 era. He replied that it's good to have a
> tidy container for engines/metadatas/sessions. But we've seen that
> SAContext's opaqueness is both a blessing and a curse. If Ben and
> Mike both endorse this application pattern, I'm inclined to just
> modify my application that way, and then I'll be less interested in
> supporting SAContext. SAContext was necessary to wean people off
> pylons.database and session_context.current.bind_to, and to provide a
> way to parse engine args. But now maybe it's no longer necessary.

I've been running Pylons 0.9.6rc2 + SA 0.4 (trunk) without SAContext
during the last two weeks or so.

Here's my setup (i'm using the assign_mapper-like behaviour):

in model/metadata.py:
---------------------
from sqlalchemy import *
from sqlalchemy.orm import *

# The metadata that will be imported and used by all tables
meta = MetaData()

# The scoped SQLAlchemy session object. This is a global object. You'll need
# a "clean" session for each request. Clean up/remove session at the request
# level, in BaseController's __call__.
Session = scoped_session(sessionmaker(autoflush=False, transactional=False))
mapper = Session.mapper
---------------------


in model/clients.py, model/users.py, model/addresses.py, etc...
---------------------------------------------------------------
from metadata import *

# ``meta`` from "metadata.py"
client_table = Table('clients', meta, ...)
class Client(object): pass

# ``mapper`` from "metadata.py" (Session's mapper: assign_mapper style)
client_mapper = mapper(Client, client_table)
---------------------------------------------------------------


in model/__init__.py:
---------------------
from metadata import meta, Session

from clients import Client
from addresses import Address
...

flush = Session.flush
delete = Session.delete
clear = Session.clear
---------------------


in lib/base.py:
---------------
from sqlalchemy import create_engine
engine = create_engine(config['sqlalchemy.dburi'],
echo=config['sqlalchemy.echo'])
model.meta.bind = engine

class BaseController(WSGIController):
def __call__(self, environ, start_response):

model.Session.close()
return WSGIController.__call__(self, environ, start_response)
---------------


in controller/clients.py:
-------------------------
class ClientsController(BaseController):
def delete(self, id):
client = model.Client.query.get(id) # assign_mapper style.
model.delete(client)
model.flush()

c.client_list = model.Client.query.all()
return render('clients/list.mako')
-------------------------

If this can inspire docs authors. :)

Regards,
--
Alexandre CONRAD

Alexandre CONRAD

unread,
Aug 15, 2007, 1:42:54 PM8/15/07
to pylons...@googlegroups.com
Alexandre CONRAD wrote:

> in lib/base.py:
> ---------------
> from sqlalchemy import create_engine
> engine = create_engine(config['sqlalchemy.dburi'],
> echo=config['sqlalchemy.echo'])
> model.meta.bind = engine
>
> class BaseController(WSGIController):
> def __call__(self, environ, start_response):
> model.Session.close()
> return WSGIController.__call__(self, environ, start_response)
> ---------------

Or

model.Session.remove()

since SA r3317.

Regards,
--
Alexandre CONRAD

Mike Orr

unread,
Aug 15, 2007, 5:06:28 PM8/15/07
to pylons...@googlegroups.com
Updating "SQLAlchemy for people in a hurry"

--
Mike Orr <slugg...@gmail.com>

Mike Orr

unread,
Aug 15, 2007, 8:38:29 PM8/15/07
to pylons...@googlegroups.com
On 8/15/07, Mike Orr <slugg...@gmail.com> wrote:
> Updating "SQLAlchemy for people in a hurry"

Finished. I need an expert to review it for accuracy, and a
pseudo-newbie to check if it explains everything clearly.

http://wiki.pylonshq.com/display/pylonscookbook/SQLAlchemy+for+people+in+a+hurry

--
Mike Orr <slugg...@gmail.com>

Michael Bayer

unread,
Aug 16, 2007, 11:28:46 AM8/16/07
to pylons-devel
ive made several changes and also released SA0.4beta3 today which
fixes the missing remove() method issue.

Michael Bayer

unread,
Aug 16, 2007, 11:43:40 AM8/16/07
to pylons-devel
how does this work ?

def pylons_scope():
import thread
from pylons import config
return "Pylons|%s|%s" % (thread.get_ident(), id(config))


isnt "config" just a regular Python module ? i dont get how
id(config) is going to change anything and/or access the whole stacked
object proxy idea. then again i havent tried it.

Ben Bangert

unread,
Aug 16, 2007, 11:58:42 AM8/16/07
to pylons...@googlegroups.com

config is a stacked object proxy, so getting it's id won't help. You
need to get the id of the object it proxies to:
id(config._current_obj())

That should work fine then.

- Ben

Alexandre CONRAD

unread,
Aug 16, 2007, 12:19:58 PM8/16/07
to pylons...@googlegroups.com
Michael Bayer wrote:

I don't undertstand this very well neither. Isn't this used to generate
a session key so a session can be called back using the key ? But what's
the point if Session.remove() is called at the end of each request ?
Plus, it's already getting the current thread's ID by default. And
id(config) will only change if the config file is modified, right ? So
the same number will be called anyway.

Regards,
--
Alexandre CONRAD

Michael Bayer

unread,
Aug 16, 2007, 12:43:52 PM8/16/07
to pylons-devel

yeah i would tend to agree. Also, I think we can bind the engine to
the Session at the configuration stage instead of in the
BaseController...since this Session object is local to your
application anyway.

Michael Bayer

unread,
Aug 16, 2007, 12:51:01 PM8/16/07
to pylons-devel
ok ive changed it to this, based on the fact that "Session" is local
to "model" and wont be referenced in other "configs":

# Global session manager. Session() returns the session object
appropriate for the current web request.
Session = scoped_session(sessionmaker(autoflush=True,
transactional=True, bind=pylons.config['pylons.g'].sa_engine))

and the controller decorate to:

def __call__(self, environ, start_response):
try:

return WSGIController.__call__(self, environ, start_response)
finally:
model.Session.remove()

*important question*: does model.py have access to
pylons.config['pylons.g'] ?


Alexandre CONRAD

unread,
Aug 16, 2007, 1:28:28 PM8/16/07
to pylons...@googlegroups.com
Michael Bayer wrote:

> *important question*: does model.py have access to
> pylons.config['pylons.g'] ?

As both BaseController and "websetup.py" (script that sets up your
application the first time) need to access the "model.py" file, I had to
do some dirty hack in "model.py" to setup the engines depending if
"model.py" was called from the pylons application or from the
"websetup.py" script.

So I just moved out the Session.configure() part from "model.py" and let
BaseController *and* "websetup.py" configure Session on their side. So
"model.py" can be correctly shared.

Regards,
--
Alexandre CONRAD

Ben Bangert

unread,
Aug 16, 2007, 1:28:34 PM8/16/07
to pylons...@googlegroups.com

The only point for this, is the extremely rare case that one should
have 2 differently configured instances of the *Same* Pylons apps in
the WSGI stack, such that each one will need a different session, but
will have the same module thread-global.

I would consider this an exceptionally rare case, and should someone
be doing that, they can keep their pylons_scope function in their own
app. No reason to put everyone else through the pain of a solution
for 0.01% of use-cases.

Cheers,
Ben

Alexandre CONRAD

unread,
Aug 16, 2007, 1:52:20 PM8/16/07
to pylons...@googlegroups.com
Alexandre CONRAD wrote:

> So I just moved out the Session.configure() part from "model.py" and let
> BaseController *and* "websetup.py" configure Session on their side. So
> "model.py" can be correctly shared.

Although, I have the following situation, which I don't really
understand. Here is my "websetup.py":


--------------------
<snip import stuff>

def setup_config(command, filename, section, vars):
"""Place any commands to setup mp here"""
conf = appconfig('config:' + filename)
load_environment(conf.global_conf, conf.local_conf)

# Setup the g object
g = config['pylons.g']

# Setup the database
print "Connecting to database %s" % repr(config['sqlalchemy.dburi'])
engine = create_engine(config['sqlalchemy.dburi'], echo=True)
# model.meta.bind = engine
model.Session.configure(bind=engine)

print "Dropping tables..."
model.meta.drop_all()
--------------------

But I get the following error:
[...]
File "[...]/sqlalchemy/schema.py", line 1245, in _get_bind
raise exceptions.InvalidRequestError("This SchemaItem is not
connected to any Engine or Connection.")

If I comment out "model.Session.configure(bind=engine)" and uncomment
"model.meta.bind = engine", it works though.

I have the following in my lib.base.py:

--------------------
<snip import stuff>

engine = create_engine(config['sqlalchemy.dburi'],
echo=config['sqlalchemy.echo'])

#model.meta.bind = engine
model.Session.configure(bind=engine)
--------------------

Of course, meta here is not called directly, but the tables seem to
figure out how it's all connected together.

Can't "meta" find out the engine through the Session ? (maybe it's not
that simple)

Here is my setup again:
http://groups.google.com/group/pylons-devel/browse_thread/thread/2b82c7093f50afc4/a6c4d7986266ddb0#a6c4d7986266ddb0

Regards,
--
Alexandre CONRAD

Michael Bayer

unread,
Aug 16, 2007, 2:29:03 PM8/16/07
to pylons-devel
just send the engine straight to meta.create_all() and
meta.drop_all(). you could also meta.bind to the engine, but then it
gets hard to figure out where the engine comes from during runtime.

MetaData otherwise knows nothing about SQLAlchemy's ORM or session
object.

Mike Orr

unread,
Aug 16, 2007, 3:59:58 PM8/16/07
to pylons...@googlegroups.com
Y'all were right about thread.get_ident(); I misremembered the syntax.

They won't know they have to. And even if they do, few people
understand the issue well enough to create a correct function, as was
just evidenced on this list. People are taught that they can plug
WSGI apps into a larger tree, which means eventually they will. A
hosting company might have an instance of the same Pylons app for each
customer; a replacement for the Confluence wiki might have one for
each "Space".

app_scope was considered important enough for pylons.database. That's
where I got it from, and that's why I included it.

I added a section near the end of the article to explain when
app_scope is needed and how to add it. I'm not sure it will "click"
with those who need it, but better to attempt some explanation than
none at all.

--
Mike Orr <slugg...@gmail.com>

Mike Orr

unread,
Aug 16, 2007, 8:51:26 PM8/16/07
to pylons...@googlegroups.com
What would be the worst-case scenario if somebody has two instances of
an app but doesn't set a scopefunc, and the session is shared between
them. Would it still work OK? Given that each thread handles only
one request at a time, does it matter if it processes an action from
one app, then the same action from the other app?

--
Mike Orr <slugg...@gmail.com>

Michael Bayer

unread,
Aug 17, 2007, 10:34:31 AM8/17/07
to pylons-devel
the current version of the wiki is setting Session, at the
"configuration" level, to point to a single engine from the config.
Each time a new session is created, it uses that same engine. if the
'config' changes to have a different engine, its not going to pick it
up since its been configured at the module level.

I know you don't like API changes but it seems here like if
scoped_session's "bind" argument could optionally be a lambda, that
would fit in quite nicely here. since im trying to avoid extra
configuration at the "BaseController" part of it.

also, i still dont like the BaseController part of the equation very
much (couldnt we use the after_request hook or whatever that is ?)

Mike Orr

unread,
Aug 17, 2007, 5:23:01 PM8/17/07
to pylons...@googlegroups.com
On 8/17/07, Michael Bayer <zzz...@gmail.com> wrote:
>
> the current version of the wiki is setting Session, at the
> "configuration" level, to point to a single engine from the config.
> Each time a new session is created, it uses that same engine. if the
> 'config' changes to have a different engine, its not going to pick it
> up since its been configured at the module level.

I think you're talking about model.Session.configure(bind=) in the
base controller or a controller method. I suspected this, but I
thought the code had come from you, and "MikeB knows best".

> I know you don't like API changes but it seems here like if
> scoped_session's "bind" argument could optionally be a lambda, that
> would fit in quite nicely here. since im trying to avoid extra
> configuration at the "BaseController" part of it.

Who doesn't like API changes? I like API changes if they're improvements.

I have no idea what you're talking about with lambda. First, bind is
a sessionmaker argument, not a scoped_session argument, right?
Second, the application would have to pack into the lambda all the
information necessary to choose the engines based on the current
request. I'm not sure that's feasible, especially if the decision is
being made in the middle of a controller method.

Of course we can use different strategies for the "one engine",
"multiple static engines", and "multiple dynamic engines" cases, but
that would be more complicated to explain. It's easier just to have a
Session.configure() that can be used in all cases and moved to any
point in the controller. Also, any solution should work for both
bind= and binds=, again to keep the explanations simple.

The easiest way out would be model.Session().configure(bind=). Two
more () makes it technically correct. But it adds inconsistency
because we're otherwise using the classmethods.

Alternatively, why not have Session.configure impose its viewpoint on
the current session? Is there any reason users would not want this?
I have a hard time believing somebody wants to configure future
sessions but leave the current session in a different state.

Or add Session.reconfigure which affects the current session.

session objects don't have a configure method last time I looked, it's
something like .bind_table or .bind_entity, which you'd have to do in
a loop for each table, and I'm not sure if there's a method to clear
all bindings. Wouldn't it make more sense to have a session.configure
method that takes care of everything in one step? Then
Session.configure could call it.

> also, i still dont like the BaseController part of the equation very
> much (couldnt we use the after_request hook or whatever that is ?)

I don't know if there's a general Pylons policy on this. I keep them
empty in the base controller so that subclasses can put their own
stuff in there and not have to call the base method.

--
Mike Orr <slugg...@gmail.com>

Michael Bayer

unread,
Aug 17, 2007, 5:53:23 PM8/17/07
to pylons-devel

On Aug 17, 5:23 pm, "Mike Orr" <sluggos...@gmail.com> wrote:
> I think you're talking about model.Session.configure(bind=) in the
> base controller or a controller method. I suspected this, but I
> thought the code had come from you, and "MikeB knows best".

OK actually, i think you might have missed this when i briefly changed
it, but Session.configure(foo=bar) is equivalent to:

Session = scoped_session(sessionmaker(foo=bar))

meaning, Session.configure() is not changing the *locally* scoped
Session, its changing the whole thing. So you definitely can't say
configure() at the request level if youre trying to support multiple
app instances.

The correct approach to the base controller version is:

def __call__():
model.Session(bind=g.sa_engine)


try:
return WSGIController.__call__(self, environ, start_response)
finally:
model.Session.remove()

Where just "instantiating" Session() (its actually another __call__()
method) sets up the engine for the current thread/app/whatever scope.

>
> I have no idea what you're talking about with lambda. First, bind is
> a sessionmaker argument, not a scoped_session argument, right?
> Second, the application would have to pack into the lambda all the
> information necessary to choose the engines based on the current
> request. I'm not sure that's feasible, especially if the decision is
> being made in the middle of a controller method.

OK, yes "bind" is an argument to sessionmaker(). both sessionmaker()
and scoped_session() return classes or callables, or more generally
"things you call which return a Session object". that means that
nothing has been configured at all until you call the return value of
either function. Since the Pylons context is a dynamically changing
contextual object, calling upon it when you need it should, if i
understand correctly, give you data thats local to *right now*. So if
you were to say (not supported yet):

Session = scoped_session(sessionmaker(autoflush=True,
transactional=True, bind=lambda:config['pylons.g'].sa_engine))

You place the call to config['pylons.g'].sa_engine in an unfired
lambda. When you actually say Session() (or use any of the
"classmethods" which do the same thing internally), the lambda gets
called. The "config['pylons.g'].sa_engine" code executes against the
current application context and you get the right engine. theres then
no need to stick the extra line in BaseController (but you still need
the remove() somewhere).


> point in the controller. Also, any solution should work for both
> bind= and binds=, again to keep the explanations simple.

whatever we're doing here, we're making sure it works the same for
bind and binds.

>
> > also, i still dont like the BaseController part of the equation very
> > much (couldnt we use the after_request hook or whatever that is ?)
>
> I don't know if there's a general Pylons policy on this. I keep them
> empty in the base controller so that subclasses can put their own
> stuff in there and not have to call the base method.

i felt that if the tutorial shows it in __after__(), its easy to type
and gets users off the ground. if they are using their own
__after__(), i think they know how to "cascade" from a subclass down
to a superclass and/or reformat how they're doing it. But thats just
my conjecture.


Ben Bangert

unread,
Aug 17, 2007, 6:40:47 PM8/17/07
to pylons...@googlegroups.com
On Aug 17, 2007, at 2:53 PM, Michael Bayer wrote:

> The correct approach to the base controller version is:
>
> def __call__():
> model.Session(bind=g.sa_engine)
> try:
> return WSGIController.__call__(self, environ, start_response)
> finally:
> model.Session.remove()
>
> Where just "instantiating" Session() (its actually another __call__()
> method) sets up the engine for the current thread/app/whatever scope.

This could be in __after__ instead, and users can call their parent
like they do should they override __call__.

> i felt that if the tutorial shows it in __after__(), its easy to type
> and gets users off the ground. if they are using their own
> __after__(), i think they know how to "cascade" from a subclass down
> to a superclass and/or reformat how they're doing it. But thats just
> my conjecture.

Agreed, I like that approach.

I should also mention that we *are* definitely maintaining the model
prefix to Session in the samples. If someone wants to pull it off
model for local use, that's their decision. Pylons has always shown
it as being under model.session.... and users like it as well. It
keeps things clear.

If we want to play with this, lets play with it on a separate (new)
wiki page, instead of constantly updating one that users are actively
using. Otherwise it drives the users crazy, or just axe it entirely
and say we're working on it, coming soon, etc.

What we've currently done is essentially take away a page that worked
great for users, and replaced it with nothing, which isn't good.
Let's restore what was there, *and* what worked, and create the
replacement elsewhere. Plus, I should also highlight, we *need* to
retain what was actually there for SAContext, because SAContext
*continues to be the ONLY effective solution* for SQLAlchemy 0.3.x
users, since all the updates are for SQLAlchemy 0.4 only.

I'd like to see the prior page on SQLAlchemy 0.3.x + SAContext
restored as the SQLAlchemy 0.3.x in a Hurry, while the new page can
be 0.4 specific. Especially since lots of users will be staying with
0.3.x for awhile, and until they all move to 0.4, SAContext isn't
dead for them yet.

Cheers,
Ben

Mike Orr

unread,
Aug 17, 2007, 7:16:00 PM8/17/07
to pylons...@googlegroups.com
On 8/17/07, Ben Bangert <b...@groovie.org> wrote:
> > i felt that if the tutorial shows it in __after__(), its easy to type
> > and gets users off the ground. if they are using their own
> > __after__(), i think they know how to "cascade" from a subclass down
> > to a superclass and/or reformat how they're doing it. But thats just
> > my conjecture.
>
> Agreed, I like that approach.

OK.

The tutorial doesn't actually show it in the tutorial anymore. It
shows sessionmaker(bind=) in the model because that's the simplest and
most common case. It mentions in "Multiple Engines" that you can put
a line in the base controller or action method,but does not actually
show where.

Do we want to do anything to accommodate .remove? Right now it's in
.__call__ because it's inside a finally:.

> If we want to play with this, lets play with it on a separate (new)
> wiki page, instead of constantly updating one that users are actively
> using. Otherwise it drives the users crazy, or just axe it entirely
> and say we're working on it, coming soon, etc.
>
> What we've currently done is essentially take away a page that worked
> great for users, and replaced it with nothing, which isn't good.
> Let's restore what was there, *and* what worked, and create the
> replacement elsewhere. Plus, I should also highlight, we *need* to
> retain what was actually there for SAContext, because SAContext
> *continues to be the ONLY effective solution* for SQLAlchemy 0.3.x
> users, since all the updates are for SQLAlchemy 0.4 only.
>
> I'd like to see the prior page on SQLAlchemy 0.3.x + SAContext
> restored as the SQLAlchemy 0.3.x in a Hurry, while the new page can
> be 0.4 specific. Especially since lots of users will be staying with
> 0.3.x for awhile, and until they all move to 0.4, SAContext isn't
> dead for them yet.

I disagree. If you want the SAContext page showing, let's put *that*
on a new wiki page.

If I were a user, I'd be pissed at a page that offered a dead-end
solution that I build into my program, and then it will be harder to
change later. So definitely don't hide the new page and keep it mum
for several days.

The other problem is we have so many inconsistent wiki pages, which
was the original problem when pylons.database was around. I want to
get those all cleaned up or deleted and up to the SQLAlchemy 0.4
standard before somebody else starts using them for a new application.

--
Mike Orr <slugg...@gmail.com>

Mike Orr

unread,
Aug 17, 2007, 7:20:21 PM8/17/07
to pylons...@googlegroups.com
On 8/17/07, Michael Bayer <zzz...@gmail.com> wrote:
> > I have no idea what you're talking about with lambda. First, bind is
> > a sessionmaker argument, not a scoped_session argument, right?
> > Second, the application would have to pack into the lambda all the
> > information necessary to choose the engines based on the current
> > request. I'm not sure that's feasible, especially if the decision is
> > being made in the middle of a controller method.
>
> OK, yes "bind" is an argument to sessionmaker(). both sessionmaker()
> and scoped_session() return classes or callables, or more generally
> "things you call which return a Session object". that means that
> nothing has been configured at all until you call the return value of
> either function. Since the Pylons context is a dynamically changing
> contextual object, calling upon it when you need it should, if i
> understand correctly, give you data thats local to *right now*. So if
> you were to say (not supported yet):
>
> Session = scoped_session(sessionmaker(autoflush=True,
> transactional=True, bind=lambda:config['pylons.g'].sa_engine))
>
> You place the call to config['pylons.g'].sa_engine in an unfired
> lambda. When you actually say Session() (or use any of the
> "classmethods" which do the same thing internally), the lambda gets
> called. The "config['pylons.g'].sa_engine" code executes against the
> current application context and you get the right engine. theres then
> no need to stick the extra line in BaseController (but you still need
> the remove() somewhere).

That won't scale to per-request bindings.

If model.Session(bind=) works, let's stick with that instead. It will
work for all scenarios, and users can move it anyplace in the
controller they want. I've already put it in the tutorial.

--
Mike Orr <slugg...@gmail.com>

Ben Bangert

unread,
Aug 17, 2007, 8:31:52 PM8/17/07
to pylons...@googlegroups.com
On Aug 17, 2007, at 4:16 PM, Mike Orr wrote:

> Do we want to do anything to accommodate .remove? Right now it's in
> .__call__ because it's inside a finally:.

I thought that would move to __after__? Since __after__ is called in
a finally already.

> I disagree. If you want the SAContext page showing, let's put *that*
> on a new wiki page.
>
> If I were a user, I'd be pissed at a page that offered a dead-end
> solution that I build into my program, and then it will be harder to
> change later. So definitely don't hide the new page and keep it mum
> for several days.

I think we need 2 pages then, so the current one is not going to work
at all. One for SA 0.3 users, and one for 0.4 users. The page title
should clearly indicate which one it is, and we can default to 0.4
with a big link at the top saying, "If you're using SA 0.3 go here".
How's that sound?

> The other problem is we have so many inconsistent wiki pages, which
> was the original problem when pylons.database was around. I want to
> get those all cleaned up or deleted and up to the SQLAlchemy 0.4
> standard before somebody else starts using them for a new application.

Absolutely. They all need to tell the same story.

Cheers,
Ben

Mike Orr

unread,
Aug 17, 2007, 8:48:17 PM8/17/07
to pylons...@googlegroups.com
On 8/17/07, Ben Bangert <b...@groovie.org> wrote:
> On Aug 17, 2007, at 4:16 PM, Mike Orr wrote:
>
> > Do we want to do anything to accommodate .remove? Right now it's in
> > .__call__ because it's inside a finally:.
>
> I thought that would move to __after__? Since __after__ is called in
> a finally already.

Oh, OK. I thought the .__after__ discussion was for
Session.configure, which wouldn't be appropriate.

> > I disagree. If you want the SAContext page showing, let's put *that*
> > on a new wiki page.
> >
> > If I were a user, I'd be pissed at a page that offered a dead-end
> > solution that I build into my program, and then it will be harder to
> > change later. So definitely don't hide the new page and keep it mum
> > for several days.
>
> I think we need 2 pages then, so the current one is not going to work
> at all. One for SA 0.3 users, and one for 0.4 users. The page title
> should clearly indicate which one it is, and we can default to 0.4
> with a big link at the top saying, "If you're using SA 0.3 go here".
> How's that sound?

OK. Can you do it, and also set .__before__ and .__after__? I'm
getting a headache today, which means I need to go to sleep early.

This would work for .__after__:

{code:python}
def __after__(self):
#model.Session.commit()
model.Session.remove()
{code}

Then a warning about enabling commit...


--
Mike Orr <slugg...@gmail.com>

Reply all
Reply to author
Forward
0 new messages