OverIQ.com

Creating URLs in Flask

Last updated on July 27, 2020


Flask can generate URLs using the url_for() function of the flask package. Hardcoding URLs in the templates and view functions is a bad practice. Suppose, we want to re-structure URLs of our blog from /<id>/<post-title>/ to /<id>/post/<post-title>/. If we had hardcoded URLs in our templates and view functions then we would have to manually visit every template and view function to make the changes. However, with the url_for() function changes like these could be done in a snap.

The url_for() function accepts the endpoint and returns the URL as a string. Recall that endpoint refers to the unique name given to URL and most of the time it is the name of the view function. At this point, main2.py contains a root ( / ) route defined as follows:

1
2
3
4
5
#...
@app.route('/')
def index():        
    return render_template('index.html', name='Jerry')
#...

To generate root URL call url_for() as url_for('index'). The output would be '/'. The following shell session demonstrates how to use url_for() inside the console.

1
2
3
4
5
6
7
8
9
>>>
>>> from main2 import app
>>> from flask import url_for
>>>
>>> with app.test_request_context('/api'): # /api is arbitrarily chosen
...    url_for('index')
...
'/'
>>>

Notice that we are first creating a request context (and thus application context implicitly). Trying to use url_for() inside the console without the request context will result in an error. You can learn more about application and request context here.

If url_for() couldn't create URL, it will throw BuildError exception.

1
2
3
4
5
6
7
8
9
>>>
>>> with app.test_request_context('/api'):
...    url_for('/api')
...
...  
werkzeug.routing.BuildError: Could not build url for endpoint '/api
Did you mean 'static' instead?
>>>
Traceback (most recent call last):

To generate the absolute URLs pass _external=True to the url_for() as follows:

1
2
3
4
5
6
>>>
>>> with app.test_request_context('/api'):
...    url_for('index', _external=True)
...
'http://localhost:5000/'
>>>

Rather than hardcoding URLs in the redirect() function you should always use url_for() to generate URLs. For example:

1
2
3
4
5
@app.route('/admin/')
def admin():
    if not loggedin:
        return redirect(url_for('login'))  # if not logged in then redirect the user to the login page
    return render_template('admin.html')

To generate URLs for dynamic routes pass dynamic parts as keyword arguments. For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
>>>
>>> with app.test_request_context('/api'):
...    url_for('user_profile', user_id = 100)
...
'/user/100/'
>>>
>>>
>>> with app.test_request_context('/api'): 
...    url_for('books', genre='biography')
...
'/books/biography/'
>>>
>>>

The extra number of keywords arguments passed to the url_for() function will be appended to the URL as a query string.

1
2
3
4
5
6
>>>
>>> with app.test_request_context('/api'):
...    url_for('books', genre='biography', page=2, sort_by='date-published')
...
'/books/biography/?page=2&sort_by=date-published'
>>>

The url_for() is one of those few functions which are available to you inside the template. To generate URLs inside templates simply call url_for() inside the double curly braces {{ ... }}, as follows:

<a href="{{ url_for('books', genre='biography') }}">Books</a>

Output:

<a href="/books/biography/">Books</a>