Hello Pylons !
as we all know, a plain jane Pylons controller typically takes a form
like this:
class HelloController(BaseController):
def index(self):
return render_response("/hello_world.html")
where "index()" returns a "Response" object, that contains the fully
rendered text of "hello_world.html".
When combining a controller with SQLAlchemy, the need to ensure that
the session is flush()ed becomes important. I see on the site theres
a description of putting the session create/flush() pair at the WSGI
request level, for example.
One pretty handy way to mark a controller method as "needs flush" or
"transactional" is through a decorator, like this:
class HelloController(BaseController):
@flush_session
def index(self):
obj = session_context.current.query(Obj).get(1)
obj.foo = 'bar'
return render_response("/hello_world.html", obj=obj)
where the "flush_session" decorator is doing something like
(extraneous decoration stuff removed):
def decorate(func):
session = session_context.current
try:
response = func()
session.flush()
return response
finally:
session.clear()
So above, "response" is the return value of "render_response()"
inside of "index()", i.e. a Response object.
Similar approaches have been taken with "transactional" methods, i.e.
begin a transaction, run the method, then commit.
however, what is wrong with these !
basically, that the full content of the response is being rendered
before the flush()/commit() takes place. Now, even though it hasnt
been sent *out* yet, and its only mildly wasteful to throw away the
response in the case the flush fails, *the flush process generates
new information*, such as new primary key values, column defaults,
etc., which its extremely likely that the response will depend on
(such as , "User "${user.name}" Saved. Click <a href="/foo?user_id=$
{user.id}">here</a> to view your new user.").
So my instinct is to modify this pattern to "defer" the response.
the controller becomes this:
class HelloController(BaseController):
@flush_session
def index(self):
obj = session_context.current.query(Obj).get(1)
obj.foo = 'bar'
return lambda: render_response("/hello_world.html", obj=obj)
and the decorator this:
def decorate(func):
session = session_context.current
try:
response = func()
session.flush()
assert callable(response), "Controller response is not a
callable"
return response()
finally:
session.clear()
where above, the render_response() call takes place *after* the
session has been flushed, where it will only occur if the flush was
successful and will have full access to any flush-generated state.
my only issue with the above is that it looks dorky, and also is a
little inconvenient and non-intuitive (although the decorator can
check that the return value is a callable to enforce the pattern).
So, question time.
- Has anyone else had this problem ? or is this not a problem ?
- is there some obvious or non-obvious way to do this more cleanly?
- if the answers are yes/no/no, would it make sense to add "deferred
render" functionality to Pylons' response mechanism ? (and possibly
for things like redirect_to(), etc?) the implementation would be
something along the lines of a DeferredResponse, or Response taking a
callable as an argument, or something along those lines. then some
other part of Pylons would have to call render() or something similar
on the response object to actually produce the content. If there
were Response /DeferredReponse, Response.render() maybe could return
"self", where DeferredResponse.render() could return "self.callable
()", which is then a Response itself.
if Pylons supported "deferred" response objects natively, this would
be more analgous to how a system like Java Struts does it; the return
value of a controller is not anything rendered at all, its just an
ActionForward object which contains *instructions* on how to render
the response. all sorts of J2EE/spring transactional managers can
cleanly wrap themselves around such a pattern, so that when the page
is being rendered, all data has been committed to the database and
all database-related state changes and new data have been produced.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups
"pylons-discuss" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at
http://groups.google.com/group/pylons-discuss?hl=en
-~----------~----~----~----~------~----~------~--~---