Thanks for sharing this Franck!

On 3 Hun, 21:25, Franck <[email protected]> wrote:
> 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...
>
> <http://www.sqlalchemy.org/docs/orm/session.html#sqlalchemy.orm.scoped...>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/sqlalchemyasa 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