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.