pythonbottlebeaker

Bottle.py session with Beaker


first time questioner here.

I'm currently struggling on how to use Beaker properly using the Bottle micro-framework. Here's the problematic program:

#!/usr/bin/python
# -*- coding: utf-8 -*-
# filename: server.py

import bottle as app
from beaker.middleware import SessionMiddleware

session_options = {
    'session.type': 'file',
    'session.data_dir': './session/',
    'session.auto': True,
}
app_middlware = SessionMiddleware(app.app(), session_options)
app_session = app.request.environ.get('beaker.session')

@app.route('/login')
def login():
    app_session = app.request.environ.get('beaker.session')
    app_session['logged_in'] = True

@app.route('/logout')
def logout():
    app_session = app.request.environ.get('beaker.session')
    if app_session.get('logged_in'):
        app_session['logged_in'] = False
        return 'You are logged out'
    app.redirect('/login')

@app.route('/dashboard')
def dashboard():
    app_session = app.request.environ.get('beaker.session')
    if app_session.get('logged_in'):
        return 'You are logged in'
    app.redirect('/login')

app.debug(True)
app.run(app=app_middlware, reloader=True)

If you noticed, I keep on calling app_session = app.request.environ.get('beaker.session') on every def block so just it will not return an error like: TypeError: 'NoneType' object does not support item assignment --- it seems that Python doesn't recognize variables that is outside its function (correct me if I'm wrong).

And here are the questions:

  1. What should I do to only have one instance of app_session = app.request.environ.get('beaker.session') so it can be available to every def blocks (I really need one instance since it's the same session to be checked and used).
  2. If this is the only way (it's ugly though), then should I just combine all routes that requires a session just so I can achieve the single instance of app_session?

Something like:

@app.route('/<path:re:(login|dashboard|logout)\/?>')
def url(path):
    app_session = app.request.environ.get('beaker.session')
    if 'login' in path:
        app_session['logged_in'] = True
    elif 'logout' in path:
        if app_session.get('logged_in'):
            # app_session.delete() it doesn't seem to work?
            app_session['logged_in'] = False
            return 'You are logged out'
        app.redirect('/login')
    elif 'dashboard' in path:
        if app_session.get('logged_in'):
            return 'You are logged in'
        app.redirect('/login')

Solution

  • Using beaker in your bottle application is easy. First, set up your Bottle app:

    import bottle
    from bottle import request, route, hook
    import beaker.middleware
    
    session_opts = {
        'session.type': 'file',
        'session.data_dir': './session/',
        'session.auto': True,
    }
    
    app = beaker.middleware.SessionMiddleware(bottle.app(), session_opts)
    

    And later on:

    bottle.run(app=app)
    

    With this in place, every time you receive a request, your Beaker session will be available as request.environ['beaker_session']. I usually do something like this for convenience:

    @hook('before_request')
    def setup_request():
        request.session = request.environ['beaker.session']
    

    This arranges to run setup_request before handling any request; we're using the bottle.request variable (see the earlier import statement), which is a thread-local variable with information about the current request. From this point on, I can just refer to request.session whenever I need it, e.g.:

    @route('/')
    def index():
        if 'something' in request.session:
           return 'It worked!'
    
        request.session['something'] = 1