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>
Load Comments