Hi,

Actually I pretty much disagree with the pattern provided here :
http://webpy.org/cookbook/sqlalchemy

<http://webpy.org/cookbook/sqlalchemy>Here's why : according to the
documentation, it's ok to instanciate *scoped_session* once : then, why use
a load hook to bind it at every request ?
http://www.sqlalchemy.org/docs/orm/session.html#sqlalchemy.orm.scoped_session

<http://www.sqlalchemy.org/docs/orm/session.html#sqlalchemy.orm.scoped_session>The
argument you supply to scoped_session constructor, i.e. *
sessionmaker(bind=engine)*, is actually a session factory which is called in
the background every time a fresh session is needed.
Here's some sample code which demonstrates that :

** meta.py **

class SessionFactory(object):

    def __init__(self, engine_factory):
        self.engine_factory = engine_factory
        self.sessionmaker = sessionmaker()

    def __call__(self):
        engine = self.engine_factory()
        self.sessionmaker.configure(bind = engine)
        session = self.sessionmaker()
        web.debug("[MODEL] Sucessfully instanciated DB session %s bound to
%s" %(session, engine))
        return session

def init_orm(engine_factory):
    return scoped_session(SessionFactory(engine_factory))

def init_engine(dsn, echo):
    engine = create_engine(dsn, echo = echo)
    web.debug("[MODEL] Successfully instantiated DB engine (DSN = %s, echo =
%s)" %(dsn, echo))
    return engine

This code allows me to :

1) Call init_engine once (or several times in the case when I need to switch
engine) and bind the result to some global variable (namely
web.config.engine)
2) Call init_orm once like this : web.config.orm = meta.init_orm(lambda :
web.config.engine)

Wrapping code :

** application.py **

class WebApplication(web.application):
    """ Web application with additional features (configuration
management...) """

    def __init__(self, mapping, fvars):

        # Parent constructor
        web.application.__init__(self, mapping, fvars)

        # The ORM is bound once since it dynamically loads the engine from
the configuration
        web.config.orm = meta.init_orm(lambda : web.config.engine)

        # SQL Alchemy processor
        self.add_processor(_sqlalchemy_processor)

    def configure(self, config_filename):

        # Reads the configuration file
        config_file = ConfigParser.ConfigParser()
        config_file.read(config_filename)

        # Initializes the components
        web.config.engine = meta.init_engine(config_file.get("sqlalchemy",
"dsn"), config_file.getboolean("sqlalchemy", "echo"))

def _sqlalchemy_processor(handler):
    """ Makes sure a commit appends at the end of each request """
    try:
        return handler()
    except web.HTTPError:
        config.orm.commit()
        raise
    except:
        config.orm.rollback()
        raise
    finally:
        config.orm.commit()

Of course this pattern can be simplified if the *engine* should be defined
once (very common scenario).

Actually I'm very eager to hear feedback about my pattern since I asked
myself the same questions as Ben.
Is the use of *web.config* OK ? Other frameworks tend to recommend the use
of a global shared *scoped_session* but I dislike that.

Thanks
Franck

On Fri, Jun 3, 2011 at 3:47 AM, Ben Hearsum <[email protected]> wrote:

> I ended up finding a solution to this: setting web.ctx.orm manually in
> setUp, and removing it in tearDown. I ended up with a Mixin class for
> my tests that looks like this:
> class
> TestDBMixin(object):
>    def
> setUp(self):
>
> db.Base.metadata.create_all(engine)
>        web.ctx.orm =
> scoped_session(sessionmaker(bind=engine))
>
>    def
> tearDown(self):
>
> db.Base.metadata.drop_all(engine)
>        web.ctx.orm =
> None
>
> And test classes that look like this:
> class TestUser(unittest.TestCase, TestDBMixin):
>    def setUp(self):
>        TestDBMixin.setUp(self)
>
>    def tearDown(self):
>        TestDBMixin.tearDown(self)
>
>    def
> test_add(self):
>        u = User('name')
>        u.add('pass')
>
> On 2 Hun, 00:04, Ben  Hearsum <[email protected]> wrote:
> > Hi,
> >
> > I'm writing a web application with web.py and sqlalchemy. I've
> usedhttp://webpy.org/cookbook/sqlalchemyas a guide to get me up and
> > running. As well as code like the tutorial, I've got Models that have
> > various methods that query the database, and Controllers that consume
> > them. The methods in my models like something like this:
> > def add(self):
> >   web.ctx.orm.add(User('blah'))
> >
> > They work perfectly fine when called by a Controller, presumably
> > because the context is flushed out at that time. However, tests always
> > fail with:
> > AttributeError: 'ThreadedDict' object has no attribute 'orm'
> >
> > I've tried all sorts of various hacks and tricks to try and make this
> > work, to no avail. I _could_ drop all of failing tests, and rely on
> > the associated Controller tests to catch any issues, but this seems
> > suboptimal.
> >
> > Is there a known solution/pattern web.py + sqlalchemy + testing?
>
> --
> You received this message because you are subscribed to the Google Groups
> "web.py" 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/webpy?hl=en.
>
>

-- 
You received this message because you are subscribed to the Google Groups 
"web.py" 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/webpy?hl=en.

Reply via email to