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.
