Torsten Irländer <
tor...@irlaender.de>
writes:
> My goal is to replace this central db connection (with credentials held as
> plaintext in the ini file) with a per request connection with credetials
> provided by the user during the login process in the webapplication. I know
> that this comes with performance issues as the connection will be
> established and closed on every request.
> ...
> Does anyone have experience in this?
Yes, I used this approach in a couple of web applications, basically
creating a registry where, keyed on ``
request.session.id``, I stored a
SA session maker bound to an engine configured explicitly for the the
requesting user.
The "sqlalchemy.url" in the configuration file is something like
sqlalchemy.url = postgresql://username:password@localhost/database
and this is the content of models/session.py:
import logging
from threading import Lock
from pyramid.httpexceptions import HTTPUnauthorized
from sqlalchemy import engine_from_config
from sqlalchemy.orm import scoped_session, sessionmaker
from zope.sqlalchemy import ZopeTransactionExtension
logger = logging.getLogger(__name__)
lock = Lock()
sessions_registry = {}
def create_session(request=None, username=None, password='', settings=None):
"""Create a new SQLAlchemy session for a user.
:param request: either ``None`` or the Pyramid request object
:param username: either ``None`` or the name of authenticating user
:param password: when authenticating, the password of the user
:param settings: either None or the Pyramid application settings
This is usually called with just the first argument, the current
`request`.
The main exception is at user login time, when the `username` and
its `password` come from the authentication panel: in this case, a
new SQLAlchemy session factory is created for the given user,
using the configuration stored in `settings`.
"""
if request is not None:
if "user_name" in request.session:
username = request.session['user_name']
password = request.session['user_password']
config = request.registry.settings
if request.registry.__name__ == 'testing':
session_id = ('cli', username)
else:
session_id =
request.session.id
else:
config = settings
session_id = ('cli', username)
lock.acquire()
try:
maker = sessions_registry.get(session_id)
if maker is None:
if username in sessions_registry:
# Remove old session
logger.debug(u'Closing old session for user "%s"', username)
old_session_id = sessions_registry[username]
del sessions_registry[username]
old_session_maker = sessions_registry[old_session_id]
old_session_maker.close()
old_session_maker.bind.dispose()
del sessions_registry[old_session_id]
url = config['sqlalchemy.url']
if username is None:
url = url.replace(u'username:password@', u'')
else:
url = url.replace(u'username', username)
url = url.replace(u'password', password)
logger.debug(u'Creating new SQLAlchemy DB engine for user "%s"',
username or u'ADMIN')
# Open a connection, to verify credentials
engine = engine_from_config(config, 'sqlalchemy.',
url=url.encode('utf-8'))
try:
engine.connect().close()
except Exception, e:
logger.warning(u'User "%s" cannot login on "%s": %s',
username or u'ADMIN',
config['sqlalchemy.url'],
str(e).split('\n')[0])
raise HTTPUnauthorized()
else:
logger.info(u'User "%s" successfully connected on "%s"',
username or u'ADMIN', config['sqlalchemy.url'])
sm = sessionmaker(autoflush=False, autocommit=False,
bind=engine,
extension=ZopeTransactionExtension())
maker = scoped_session(sm)
sessions_registry[session_id] = maker
sessions_registry[username] = session_id
finally:
lock.release()
return maker()
Then everything, from initializedb to all the views, use this function
to get a SA session.
Maybe there are better ways to accomplish the task, but the above worked
great for me.
Hope this helps,
ciao, lele.
--
nickname: Lele Gaifax | Quando vivrò di quello che ho pensato ieri
real: Emanuele Gaifas | comincerò ad aver paura di chi mi copia.
le...@metapensiero.it | -- Fortunato Depero, 1929.