How to remove SA mods.threadlocal and objectstore

4 views
Skip to first unread message

Joe

unread,
Sep 19, 2006, 9:21:24 PM9/19/06
to pylons-...@googlegroups.com
Hi,

I'm trying to update the QuickWiki tutorial code by following the
recommendation from Mike Bayer to use sessions instead of
mods.threadlocal (and so that I can use it in my own app).

I modified the BaseController as follows:

---
from sqlalchemy import *

class BaseController(WSGIController):
def __call__(self, environ, start_response):
conn = model.meta.connect(
request.environ['paste.config']['app_conf']['dsn']
)
sess = create_session(bind_to=conn)
sess.clear()
response = WSGIController.__call__(self, environ,
start_response)
sess.flush()
return response
---

This appears to work and I'm able to edit a wiki page and it appears to
save it, but it doesn't really. The problem is that in page.py, the
__before__() and delete() methods use objecstore. SA has an
object_session() function that can be used to obtain the session for an
object, but I'm not sure what object to use. I'm hoping someone could
offer some guidance (and comment on the code above, too).

On a related note, is there some reason for the pylons.database module
providing "convenient access" only to a SQLObject-managed database,
other than SO being there first?

Joe

Waldemar Osuch

unread,
Sep 19, 2006, 10:07:13 PM9/19/06
to pylons-discuss
Joe wrote:
> Hi,
>
> I'm trying to update the QuickWiki tutorial code by following the
> recommendation from Mike Bayer to use sessions instead of
> mods.threadlocal (and so that I can use it in my own app).
>
> I modified the BaseController as follows:
>
> ---
> from sqlalchemy import *
>
> class BaseController(WSGIController):
> def __call__(self, environ, start_response):
> conn = model.meta.connect(
> request.environ['paste.config']['app_conf']['dsn']
> )
> sess = create_session(bind_to=conn)
> sess.clear()
> response = WSGIController.__call__(self, environ,
> start_response)
> sess.flush()
> return response
> ---
>

The approach I have taken when going trought tutorial recently is to
define database connection in my model pages.py like this
===================================================
from sqlalchemy import *
import re

from docutils.core import publish_parts
import quickwiki.lib.helpers as h

wikiwords = re.compile(r"\b([A-Z]\w+[A-Z]+\w+)")
meta = BoundMetaData('sqlite:///qwicki.db')

page = Table('page', meta,
Column('title', String(40), primary_key=True),
Column('content', String(), default='')
)

meta.create_all()

class Page(object):
content = None

def __str__(self):
return self.title

def get_wiki_content(self):
content = publish_parts(self.content,
writer_name="html")["html_body"]
titles = wikiwords.findall(content)
if titles:
for title in titles:
content = content.replace(
title,
h.link_to(
title,
h.url_for(
controller='page',
action='index',
title=title
),
),
)
return content

mapper(Page, page)
--------------------------------------------------------------------------------

and the controller pages.py looks like this
==============================================
from qwiki.lib.base import *

from sqlalchemy import create_session

class PageController(BaseController):
def __before__(self):
db = self.db = create_session()
self.qry = db.query(model.Page)

def __after__(self):
self.db.close()

def index(self, title):
pg = self.qry.get_by(title=title)
if pg:
c.content = pg.get_wiki_content()
return render_response('/page.myt')
elif model.wikiwords.match(title):
return render_response('/new_page.myt')
abort(404)

def edit(self, title):
pg = self.qry.get_by(title=title)
if pg:
c.content = pg.content
return render_response('/edit.myt')

def save(self, title):
pg = self.qry.get_by(title=title)
if not pg:
pg = model.Page()
pg.title = title
pg.content = request.params['content']
c.title = pg.title
c.content = pg.get_wiki_content()
c.message = 'Content Saved'
self.db.save_or_update(pg)
self.db.flush()
return render_response('/page.myt')

def list(self):
c.titles = (pg.title for pg in self.qry.select())
return render_response('/titles.myt')

def delete(self):
title = request.params['id'][:5]
pg = self.qry.get_by(title)
self.db.delete(pg)
self.db.flush()
c.titles = (pg.title for pg in self.qry.select())
return render_response('/list.myt', fragment=True)
=================================================

I did not touch BaseController prefering to keep database session
locally where I can see it :-)

It did seem to work OK. At least for the tutorial purposes.
The only bad part is that the path to the database is hardcoded in
models/page.py.
I would prefer to get it from configuration file but I could not figure
out
where all the settings live. Should be somwhere in paste I think.

Waldemar

Jose Galvez

unread,
Sep 19, 2006, 10:20:15 PM9/19/06
to pylons-...@googlegroups.com
This is great, I was having trouble going from the tutorial to my own
project, getting some funky session errors, I am going to give your code
a try and see if that makes it work
Jose
Message has been deleted

Michael G. Noll

unread,
Sep 29, 2006, 8:44:44 AM9/29/06
to pylons-discuss
Joe wrote:
> This appears to work and I'm able to edit a wiki page and it appears to
> save it, but it doesn't really. The problem is that in page.py, the
> __before__() and delete() methods use objecstore.

Are you explicitly saving [1] newly created objects (such as your wiki
page) to the SQLAlchemy session where needed? The threadlocal plugin
[2] will automagically do this for you, so if you remove it, you have
to do it manually or flush'ing a session wil do nothing.

> SA has an object_session() function that can be used to obtain the
> session for an object, but I'm not sure what object to use.

You might want to read SQLAlchemy's documentation on sessions [3]. This
should get you started.

Here is a small, exemplary snippet of code to show you how to create
and save objects without threadlocal magic in SQLAlchemy:

---------------------- snipp -------------------
page = WikiPage(name='PylonsForRunaways')
session.save(page) # save page to session
session.flush() # write session changes to the database
---------------------- snipp -------------------

Hope that helps,
Michael

[1] http://www.sqlalchemy.org/docs/unitofwork.myt#unitofwork_api_save
[2] http://www.sqlalchemy.org/docs/plugins.myt#plugins_threadlocal
[3] http://www.sqlalchemy.org/docs/unitofwork.myt

Daniel Lyons

unread,
Sep 29, 2006, 7:15:48 PM9/29/06
to pylons-...@googlegroups.com

Here is a small, exemplary snippet of code to show you how to create
and save objects without threadlocal magic in SQLAlchemy:

---------------------- snipp -------------------
page = WikiPage(name='PylonsForRunaways')
session.save(page) # save page to session
session.flush () # write session changes to the database
---------------------- snipp -------------------

I kind of discovered this myself when going through the SQLAlchemy docs. I dislike the extra lines of code in every controller. Maybe there's a way to get threadlocal-like functionality in Python 2.5 by wrapping the Controller method invocation in a 'with' block that does the setup and teardown and only have to add the "session.save(page)" stuff or, even better, discovering which objects were instantiated.

In the worst case it would probably be possible to create a common parent class that would catch the instantiations and provide them via a singleton to the Controller invocation wrapper. But I haven't looked at it too closely.

I did run into problems even with SessionContext in my threaded app. It seemed like I needed to re-fetch all of the pertinent objects from the new thread; I got exceptions saying the objects were associated with the wrong context otherwise. I'm sure it's covered in the SQLAlchemy docs, which I've been skimming more than closely reading.

--
Daniel
http://www.storytotell.org -- Tell It!
Reply all
Reply to author
Forward
0 new messages