Skip to content

Commit

Permalink
Let pyramid_tm close the connection for us, preventing race condition…
Browse files Browse the repository at this point in the history
…s with commit vs. close
  • Loading branch information
Marco Martinez committed Jun 24, 2016
1 parent ad7163d commit cb075a7
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 23 deletions.
1 change: 0 additions & 1 deletion occams/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@ def main(global_config, **settings):
config.include('pyramid_chameleon')
config.include('pyramid_redis')
config.include('pyramid_redis_sessions')
config.include('pyramid_tm')
config.include('pyramid_webassets')
config.add_renderer('json', JSON(
adapters=(
Expand Down
79 changes: 57 additions & 22 deletions occams/models.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import sqlalchemy as sa
from sqlalchemy import engine_from_config
from sqlalchemy.orm import sessionmaker
import zope.sqlalchemy

Expand All @@ -7,34 +7,69 @@
from . import log


def includeme(config):
"""
Configures database connection
def get_engine(settings, prefix='sqlalchemy.'):
engine = engine_from_config(settings, prefix)
log.info('Connected to: "%s"' % repr(engine.url))
return engine


def get_session_factory(engine):
factory = sessionmaker()
factory.configure(bind=engine)
return factory


def get_tm_session(session_factory, transaction_manager, info=None):
"""
settings = config.registry.settings
Get a ``sqlalchemy.orm.Session`` instance backed by a transaction.
maker = sessionmaker()
zope.sqlalchemy.register(maker)
occams_datastore.models.events.register(maker)
This function will hook the session to the transaction manager which
will take care of committing any changes.
engine = sa.engine_from_config(settings, 'occams.db.')
maker.configure(bind=engine)
- When using pyramid_tm it will automatically be committed or aborted
depending on whether an exception is raised.
config.registry['db_sessionmaker'] = maker
config.add_request_method(_get_db_session, 'db_session', reify=True)
- When using scripts you should wrap the session in a manager yourself.
For example::
log.info('Connected to: "%s"' % repr(engine.url))
import transaction
engine = get_engine(settings)
session_factory = get_session_factory(engine)
with transaction.manager:
dbsession = get_tm_session(session_factory, transaction.manager)
def _get_db_session(request):
db_session = request.registry['db_sessionmaker']()
# Keep track of the request so we can generate model URLs
db_session.info['request'] = request
db_session.info['settings'] = request.registry.settings
"""
dbsession = session_factory()

if info:
dbsession.info.update(info)

occams_datastore.models.events.register(dbsession)
zope.sqlalchemy.register(
dbsession, transaction_manager=transaction_manager)
return dbsession


def includeme(config):
"""
Initialize the model for a Pyramid app.
"""
settings = config.get_settings()

def close_session(request):
db_session.close()
# use pyramid_tm to hook the transaction lifecycle to the request
config.include('pyramid_tm')

request.add_finished_callback(close_session)
engine = get_engine(settings, prefix='occams.db.')
session_factory = get_session_factory(engine)
config.registry['dbsession_factory'] = session_factory

return db_session
# make request.dbsession available for use in Pyramid
config.add_request_method(
# r.tm is the transaction manager used by pyramid_tm
lambda r: get_tm_session(
session_factory,
r.tm, info={'request': r, 'settings': r.registry.settings}),
'db_session',
reify=True
)

0 comments on commit cb075a7

Please sign in to comment.