Explore Flask

6.2. URL Converters

6.2.1. Built-in converters

When you define a route in Flask, you can specify parts of it that will be converted into Python variables and passed to the view function.

@app.route('/user/<username>')
def profile(username):
    pass

Whatever is in the part of the URL labeled <username> will get passed to the view as the username argument. You can also specify a converter to filter the variable before it's passed to the view.

@app.route('/user/id/<int:user_id>')
def profile(user_id):
    pass

In this code block, the URL http://myapp.com/user/id/Q29kZUxlc3NvbiEh will return a 404 status code - not found. This is because the part of the URL that is supposed to be an integer is actually a string.

We could have a second view that looks for a string as well. That would be called for /user/id/Q29kZUxlc3NvbiEh/ while the first would be called for /user/id/124.

This table shows Flask's built-in URL converters.

Converter Description
string Accepts any text without a slash (the default).
int Accepts integers.
float Like int but for floating point values.
path Like string but accepts slashes.

6.2.2. Custom converters

We can also make custom converters to suit our needs. On Reddit — a popular link sharing site — users create and moderate communities for theme-based discussion and link sharing. Some examples are /r/python and /r/flask, denoted by the path in the URL: reddit.com/r/python and reddit.com/r/flask respectively. An interesting feature of Reddit is that you can view the posts from multiple subreddits as one by seperating the names with a plus-sign in the URL, e.g. reddit.com/r/python+flask.

We can use a custom converter to implement this feature in our own Flask apps. We'll take an arbitrary number of elements separated by plus-signs, convert them to a list with a ListConverter class and pass the list of elements to the view function.

# myapp/util.py

from werkzeug.routing import BaseConverter

class ListConverter(BaseConverter):

    def to_python(self, value):
        return value.split('+')

    def to_url(self, values):
        return '+'.join(BaseConverter.to_url(value)
                        for value in values)

We need to define two methods: to_python() and to_url(). As the names suggest, to_python() is used to convert the path in the URL to a Python object that will be passed to the view and to_url() is used by url_for() to convert arguments to their appropriate forms in the URL.

To use our ListConverter, we first have to tell Flask that it exists.

# /myapp/__init__.py

from flask import Flask

app = Flask(__name__)

from .util import ListConverter

app.url_map.converters['list'] = ListConverter

Caution This is another chance to run into some circular import problems if your util module has a from . import app line. That's why I waited until app had been initialized to import ListConverter.

Now we can use our converter just like one of the built-ins. We specified the key in the dictionary as "list" so that's how we use it in @app.route().

# myapp/views.py

from . import app

@app.route('/r/<list:subreddits>')
def subreddit_home(subreddits):
    """Show all of the posts for the given subreddits."""
    posts = []
    for subreddit in subreddits:
        posts.extend(subreddit.posts)

    return render_template('/r/index.html', posts=posts)

This should work just like Reddit's multi-reddit system. This same method can be used to make any URL converter we can dream of.