Skip to main content

Routing in Flask: Variable Rules and URL Building

We've successfully created a simple Flask application that can serve a response from a single URL. But a real website has many pages: an about page, a contact page, user profiles, and so on. The system that maps URLs to the Python functions that handle them is called routing.

Flask's routing system is powerful and flexible. It allows you to create both static routes (like /about) and dynamic routes that can capture parts of a URL as a variable (like /user/alice).


📚 Prerequisites

You should have a minimal Flask application set up and understand the basics of the @app.route() decorator.


🎯 Article Outline: What You'll Master

In this article, you will learn:

  • Static Routes: How to add multiple, simple pages to your application.
  • Variable Rules (Dynamic Routes): How to create routes that capture values from the URL (e.g., user IDs, names).
  • URL Converters: How to specify the data type of a variable in a URL (e.g., string, int, float).
  • URL Building with url_for(): The proper, robust way to create links to your application's routes.

🧠 Section 1: Static Routes

A static route is the simplest kind. It's a fixed path that maps directly to a view function. We've already seen this with our root route (/). Let's add another one for an "about" page.

from flask import Flask

app = Flask(__name__)

@app.route("/")
def index():
return "<h1>Home Page</h1>"

# A new static route
@app.route("/about")
def about():
return "<h2>This is the About Page.</h2>"

Now, if you run your app, visiting / will show the home page, and visiting /about will show the about page.


💻 Section 2: Dynamic Routes with Variable Rules

What if you want to create a profile page for every user on your site? You don't want to write @app.route("/user/alice"), @app.route("/user/bob"), etc., for every user.

You can make parts of a URL dynamic by marking them with <variable_name>. This captured value is then passed as a keyword argument to your view function.

from flask import Flask, escape

app = Flask(__name__)

# The <username> part is a variable
@app.route("/user/<username>")
def show_user_profile(username):
# We use escape() to prevent security issues if the username contains HTML
return f"<h1>Profile page for user: {escape(username)}</h1>"

Now, if you navigate to /user/Alice, the show_user_profile function is called with username='Alice'. If you go to /user/Bob, it's called with username='Bob'.

Using Converters

By default, a variable in a route is treated as a string. But you can specify a different type using a converter. The syntax is <converter:variable_name>.

This is useful for validation. If a user tries to visit /post/hello, but your route expects an integer, Flask will automatically return a 404 Not Found error.

Common Converters:

  • string: (Default) Accepts any text without a slash.
  • int: Accepts positive integers.
  • float: Accepts positive floating-point numbers.
  • path: Like string, but also accepts slashes.

Example:

@app.route("/post/<int:post_id>")
def show_post(post_id):
# post_id is guaranteed to be an integer here
return f"<h2>Showing post with ID: {post_id}</h2>"

@app.route("/path/<path:subpath>")
def show_subpath(subpath):
return f"<h3>Path provided: {escape(subpath)}</h3>"

🛠️ Section 3: Building URLs with url_for()

It's very bad practice to hard-code URLs in your application (e.g., writing <a href="/about">About</a> in a template). If you ever decide to change the /about route to /about-us, you would have to find and replace every single link.

Flask provides the url_for() function to solve this. url_for() builds a URL to a specific function dynamically. You give it the name of the view function, and it returns the URL path.

Why use url_for()?

  • Maintainability: It's much more robust. If you change a route's URL, all your links will update automatically.
  • Clarity: url_for('show_user_profile', username='dave') is more descriptive than '/user/dave'.
  • Handles Escaping: It correctly handles escaping of special characters.

Example: Let's create a simple navigation menu in a template using url_for().

app.py:

from flask import Flask, render_template, url_for

app = Flask(__name__)

@app.route("/")
def index():
# We'll pass the function names to the template for demonstration
return render_template("nav.html")

@app.route("/user/<username>")
def user_profile(username):
return f"<h1>Hello, {username}</h1>"

templates/nav.html:

<!DOCTYPE html>
<html>
<body>
<h1>Navigation</h1>
<nav>
<ul>
<!-- url_for() takes the VIEW FUNCTION'S NAME as its argument -->
<li><a href="{{ url_for('index') }}">Home</a></li>

<!-- For dynamic routes, pass the variable as a keyword argument -->
<li><a href="{{ url_for('user_profile', username='Alice') }}">Alice's Profile</a></li>
<li><a href="{{ url_for('user_profile', username='Bob') }}">Bob's Profile</a></li>
</ul>
</nav>
</body>
</html>

When you run this, Flask and Jinja2 will work together. The url_for('user_profile', username='Alice') call will be replaced with /user/Alice in the final HTML sent to the browser. If you ever change the route to @app.route("/profile/<username>"), this template will still work perfectly without any changes.


✨ Conclusion & Key Takeaways

Effective routing is the backbone of any web application. Flask's system of using decorators for static routes, variable rules for dynamic content, and the url_for() function for robust link building provides a powerful and easy-to-use foundation.

Let's summarize the key takeaways:

  • Use @app.route() to map a URL to a view function.
  • Use <variable_name> to capture dynamic parts of a URL.
  • Use converters like <int:variable_name> to validate the data type in the URL.
  • Always use url_for('view_function_name', ...) to build URLs in your templates and redirects. Never hard-code them.

➡️ Next Steps

We've seen how to get data from the URL. But how do we handle data sent to our application, for example, from a login form? In the next article, we'll explore the "Request and Response Objects in Flask" to see how we can access incoming request data.

Happy routing!