Implementing breadcrumbs in Python using Flask?

So you're after "path/history" breadcrumbs, rather than "location" breadcrumbs to use the terminology from the wikipedia article?

If you want to have access to the user's history of visited links, then you're going to have to save them in a session. I've had a go at creating a decorator to do this.

breadcrumb.py:

import functools
import collections

import flask

BreadCrumb = collections.namedtuple('BreadCrumb', ['path', 'title'])

def breadcrumb(view_title):
    def decorator(f):
        @functools.wraps(f)
        def decorated_function(*args, **kwargs):
            # Put title into flask.g so views have access and
            # don't need to repeat it
            flask.g.title = view_title
            # Also put previous breadcrumbs there, ready for view to use
            session_crumbs = flask.session.setdefault('crumbs', [])
            flask.g.breadcrumbs = []
            for path, title in session_crumbs:
                flask.g.breadcrumbs.append(BreadCrumb(path, title))

            # Call the view
            rv = f(*args, **kwargs)

            # Now add the request path and title for that view
            # to the list of crumbs we store in the session.
            flask.session.modified = True
            session_crumbs.append((flask.request.path, view_title))
            # Only keep most recent crumbs (number should be configurable)
            if len(session_crumbs) > 3:
                session_crumbs.pop(0)

            return rv
        return decorated_function
    return decorator

And here's a test application that demonstrates it. Note that I've just used Flask's built-in client side session, you'd probably want to use a more secure server-side session in production, such as Flask-KVsession.

#!/usr/bin/env python
import flask
from breadcrumb import breadcrumb

app = flask.Flask(__name__)

@app.route('/')
@breadcrumb('The index page')
def index():
    return flask.render_template('page.html')

@app.route('/a')
@breadcrumb('Aardvark')
def pagea():
    return flask.render_template('page.html')

@app.route('/b')
@breadcrumb('Banana')
def pageb():
    return flask.render_template('page.html')

@app.route('/c')
@breadcrumb('Chimp')
def pagec():
    return flask.render_template('page.html')

@app.route('/d')
@breadcrumb('Donkey')
def paged():
    return flask.render_template('page.html')

if __name__ == '__main__':
    app.secret_key = '83cf5ca3-b1ee-41bb-b7a8-7a56c906b05f'
    app.debug = True
    app.run()

And here's the contents of templates/page.html:

<!DOCTYPE html>
<html>
    <head><title>{{ g.title }}</title></head>
    <body>
        <h1>{{ g.title }}</h1>
        <p>Breadcrumbs:
        {% for crumb in g.breadcrumbs %}
            <a href="{{ crumb.path }}">{{ crumb.title }}</a>
            {% if not loop.last %}&raquo;{% endif %}
        {% endfor %}
        </p>
        <p>What next?</p>
        <ul>
            <li><a href="/a">Aardvark</a>?</li>
            <li><a href="/b">Banana</a>?</li>
            <li><a href="/c">Chimp</a>?</li>
            <li><a href="/d">Donkey</a>?</li>
        </ul>
    </body>
</html>