Pylons includes a session object: a session is a server-side, semi-permanent storage for data associated with a client.
The Session Object¶
The Pylons session object is available at
modules created via paster controller/restcontroller import the
session object by default.
The basic session API is simple, it implements a dict-like interface with a few additional methods. The following is an example of using the session to store a token identifying if a client is logged in.
class LoginController(BaseController): def authenicate(self): name = request.POST['name'] password = request.POST['password'] user = Session.query(User).filter_by(name=name, password=password).first() if user: msg = 'Successfully logged in as %s' % name location = url('index') session['logged_in'] = True session.save() else: msg = 'Invalid username/password' location = url('login') flash(msg) redirect(location) def logout(self): # Clear all values in the session associated with the client session.clear() session.save()
Subsequent requests can then determine if a client is logged in or not by checking the session:
if not session.get('logged_in'): flash('Please login') redirect(url('login'))
The session object acts lazily: it does not load the session data (from disk or
whichever backend is used) until the data is first accessed. This lazyness is
facilitated via an intermediary
wraps the actual
beaker.session.Session object. Furthermore the
session will not write changes to its backend without an explicit call to its
beaker.session.Session.save() method (unless configured with the
Session data is generally serialized for storage via the Python
module, so anything stored in the session must be pickleable.
The lightweight SessionObject wrapper is created by the:
beaker.middleware.SessionMiddleware WSGI middleware. SessionMiddleware
stores the wrapper in the WSGI environ where Pylons sets a reference to it from
Sessions are associated with a client via a client-side cookie. The WSGI middleware is also responsible for sending said cookie to the client.
Configuring the Session¶
The basic session defaults are:
- File based sessions (session data is stored on disk)
- Session cookies have no expiration date (cookies expire at the end of the browser session)
- Session cookie domain/path matches the current host/path
Pylons projects by default sets the following couple of session options via their .ini files. All Beaker specific session options in the ini file are prefixed with beaker.session:
cache_dir = %(here)s/data beaker.session.key = foo beaker.session.secret = somesecret
cache_dir acts a base directory for both session and cache storage. Session
data is stored in this location under a
session.key is the name attribute of the cookie sent to the browser. This
defaults to your project’s name.
session.secret is the secret token used to hash the cookie data sent to the
client. This should be a secret, ideally randomly generated value on production
environments. paster make-config will generate a random secret for
you when creating a production ini file.
Other Session Options¶
Some other commonly used session options are:
typeThe type of the back-end for storing session data. Beaker supports many different backends, see Beaker Configuration Documentation for the choices. Defaults to ‘file’.
cookie_domainThe domain name to use for the session Cookie. For example, when using sub-domains, set this to the parent domain name so that the cookie is valid for all sub-domains.
To enable pure Cookie-based Sessions and force the cookie domain to be valid for all sub-domains of ‘example.com’, add the following to your Pylons ini file:
beaker.session.type = cookie beaker.session.cookie_domain = .example.com
See the Beaker Configuration Documentation for an exhaustive list of Session options.
Storing SQLAlchemy mapped objects in Beaker sessions¶
Mapped objects from SQLAlchemy can be serialized into the beaker session, but
care must be taken when retrieving these objects back from the beaker
session. They will not be associated with the SQLAlchemy Unit-of-Work Session,
however these objects can be reconciled via the SQLAlchemy Session’s
method, as follows:
address = DBSession.query(Address).get(id) session[id] = address ... address = session.get(id) address = DBSession.merge(address)
Custom and caching middleware¶
Care should be taken when deciding in which layer to place custom middleware. In most cases middleware should be placed between the Pylons WSGI application instantiation and the Routes middleware; however, if the middleware should run before the session object or routing is handled:
# Routing/Session Middleware app = RoutesMiddleware(app, config['routes.map']) app = SessionMiddleware(app, config) # MyMiddleware can only see the cache object, nothing *above* here app = MyMiddleware(app) app = CacheMiddleware(app, config)
Some of the Pylons middleware layers such as the
Cache middleware, only add
objects to the environ dict, or add HTTP headers to the response (the Session middleware for
example adds the session cookie header). Others, such as the
Status Code Redirect, and the
Handler may fully intercept the request entirely, and change how its responded to.
Using Session in Internationalization¶
How to set the language used in a controller on the fly.
For example this could be used to allow a user to set which language they wanted your application to work in. Save the value to the session object:
session['lang'] = 'en' session.save()
then on each controller call the language to be used could be read from the
session and set in the controller’s
__before__() method so that the pages
remained in the same language that was previously set:
def __before__(self): if 'lang' in session: set_lang(session['lang'])
Using Session in Secure Forms¶
Authorization tokens are stored in the client’s session. The web app can then verify the request’s submitted authorization token with the value in the client’s session.
This ensures the request came from the originating page. See the wikipedia entry for Cross-site request forgery for more information.
Pylons provides an
authenticate_form decorator that does this verification
on the behalf of controllers.
These helpers depend on Pylons’
session object. Most of them can be easily
ported to another framework by changing the API calls.
Using middleware (Beaker) with a composite app¶
How to allow called WSGI apps to share a common session management utility.
(From a paste #616 baked by Mark Luffel)
# Here's an example of configuring multiple apps to use a common # middleware filter # The [app:home] section is a standard pylons app # The ``/servicebroker`` and ``/proxy`` apps both want to be able # to use the same session management [server:main] use = egg:Paste#http host = 0.0.0.0 port = 5000 [filter-app:main] use = egg:Beaker#beaker_session next = sessioned beaker.session.key = my_project_key beaker.session.secret = i_wear_two_layers_of_socks [composite:sessioned] use = egg:Paste#urlmap / = home /servicebroker = servicebroker /proxy = cross_domain_proxy [app:servicebroker] use = egg:Appcelerator#service_broker [app:cross_domain_proxy] use = egg:Appcelerator#cross_domain_proxy [app:home] use = egg:my_project full_stack = true cache_dir = %(here)s/data