Routing and Views in Flask

Hey there, readers! Welcome back! In the last post, we explored structuring and debugging a Flask application. Now that we’ve got our blog project structure sorted, it’s time to dive into one of the most important aspects of web development: routing.

Routing is what connects the URLs users type into their browsers to the functionality behind your app. Whether displaying a homepage, rendering individual blog posts, or even handling errors gracefully, routing is the magic that makes it all happen.

In this post, we’ll break down how Flask handles routing, explore its features, and set up views that bring your application to life—all in an easy-to-follow, beginner-friendly way. Let’s get started!

Featured image of Routing and Views in Flask tutorial

Here’s what we’ll explore:

URL Routing in Flask

In Flask, routing is handled using the @app.route() decorator. This handy tool links a specific URL to a view function, allowing you to define what happens when a user visits that URL.

Mapping URLs to Functions with @app.route()

Here’s an example for our Personal Blog Application:

from flask import Flask

app = Flask(__name__)

@app.route('/')
def home():
    return "Welcome to My Blog!"

@app.route('/about')
def about():
    return "About My Blog"

if __name__ == "__main__":
    app.run()

The @app.route() decorator maps the URL ('/' and '/about') to their respective view functions (home and about).

Visiting / will display “Welcome to My Blog!”, while /about will show “About My Blog”.

Output:

Screenshot of the Flask application home page displaying the message 'Welcome to My Blog

Options and Parameters for @app.route()

This @app.route() decorator comes with several powerful options and parameters that help you to customize how routes behave. Let’s go over these parameters one by one with easy examples.

1. rule: The URL Path

The rule is the actual URL that users will type into their browser to reach a particular page in your app. For example, we have created a route for an About page:

@app.route('/about')
def about():
    return "Welcome to the About page!"

2. methods: Specify Allowed HTTP Methods

By default, Flask only allows GET requests for routes. But what if you want to accept other types of requests, like POST? You can use the methods parameter to specify which HTTP methods are allowed for the route.

Let’s look at an example where we want to accept POST requests for submitting a form:

@app.route('/submit', methods=['POST'])
def submit():
    return "Your form has been submitted!"

In this case, only POST requests will work. If someone tries to access the /submit route using (by directly typing URL) GET, Flask will give a 405 error, telling them that the method is not allowed.

Output:

Screenshot of a Flask application page displaying a 'Method Not Allowed' error, demonstrating how to handle HTTP method restrictions in Flask.

3. endpoint: Custom Name for the Route

The endpoint parameter lets you give a custom name to a route. This can be helpful if you need to generate URLs dynamically in your app. You can use the url_for() function to do this.

Here’s an example:

@app.route('/profile', endpoint='user_profile')
def profile():
    return "This is your profile!"

Now, instead of hardcoding the URL /profile, you can use url_for('user_profile') to get the URL dynamically, which makes your code more flexible and cleaner.

4. strict_slashes: Handle Trailing Slashes

The strict_slashes parameter helps Flask decide if it should treat URLs with or without a trailing slash as the same. By default, Flask treats them as same.

For example, if you have /home and /home/, Flask will treat them as same routes unless you set strict_slashes=True.

Let’s look at this in action:

from flask import Flask

app = Flask(__name__)

@app.route('/home', strict_slashes=True)
def home():
    return "Welcome to My Blog!"

@app.route('/about/', strict_slashes=False)
def about():
    return "About My Blog"

@app.route('/submit', methods=['POST'])
def submit():
    return "Your form has been submitted!"

if __name__ == "__main__":
    app.run()

Output:

Defining Multiple Routes for a Single View

Sometimes, you may want the same function to handle multiple URLs. To do that, you have to use multiple @app.route() decorators for each URL path you want to map to the same view function.

from flask import Flask

app = Flask(__name__)

@app.route('/')
@app.route('/dashboard')
@app.route('/home', strict_slashes=True)
def home():
    return "Welcome to My Blog!"

@app.route('/about/', strict_slashes=False)
def about():
    return "About My Blog"

@app.route('/submit', methods=['POST'])
def submit():
    return "Your form has been submitted!"

if __name__ == "__main__":
    app.run()

Here, /, dashboard and /home will display “Welcome to My Blog”.

Output:

Dynamic URL Parameters in Flask

So far, we’ve learned how to create routes with the @app.route() decorator and explored the options it offers. Now, let’s take it a step further. Flask also allows you to make routes more dynamic by adding URL parameters. These parameters let you include data directly in the URL, making handling things like blog post IDs, usernames, or product prices easy.

Adding Dynamic Parameters to Routes

To include dynamic parameters in a route, you simply use angle brackets (< >) in the URL path. Flask then extracts the parameter value from the URL and passes it to the associated view function.

Here’s how you can define a route that includes a dynamic parameter:

@app.route('/post/<id>')
def show_post(id):
    return f"Showing details for post ID: {id}"

Output:

If a user visits /post/42, Flask will extract 42 as the id and display:

Screenshot of a Flask application displaying a blog post. The URL includes dynamic parameters to fetch and display the specific blog post based on its unique ID.

By default, Flask treats all dynamic parameters as strings unless you specify otherwise. So let’s move on and see how to specify other data types for parameters.

Data Types for URL Parameters

In Flask, when you create dynamic URL parameters, you can also define what kind of data you expect from them. This helps you make sure that the data coming in is the right type. For example, you wouldn’t want a string where you expect a number, right?

Here’s a simple table showing the different types of data you can use for URL parameters:

Data TypeWhat It DoesHow to Use ItExample
StringMatches any kind of text (this is the default)<name>/user/john
IntegerOnly matches whole numbers (no decimals)<int:id>/post/42
FloatMatches numbers with decimals (like prices)<float:price>/product/12.99
PathMatches text that includes slashes (/)<path:subpath>/docs/chapter1/introduction

Example:

@app.route('/user/<string:username>')
def user_profile(username):
    return f"Hello, {username}! Welcome to your profile."

@app.route('/post/<int:post_id>')
def post_details(post_id):
    return f"Viewing details for post #{post_id}."

@app.route('/product/<float:price>')
def product_details(price):
    return f"The product costs ${price:.2f}."

@app.route('/docs/<path:subpath>')
def documentation(subpath):
    return f"You are viewing: {subpath}"

Output:

Validating URL Parameters in Views

Let’s say we want to create a route that shows a specific blog post based on the id parameter in the URL. We also want to make sure that the ID is a positive integer, and if it’s not, we’ll show an error message.

# A simple route to display a post based on its ID
@app.route('/post/<int:id>')
def show_post(id):
    # Check if the id is valid
    if id <= 2:
        return "Invalid post ID! Please enter a valid number.", 400  # Return error if id is invalid
    
    # If the ID is valid, display the post (we'll simulate post retrieval here)
    return f"Displaying blog post with ID: {id}"

Output:

Providing Default Values for Parameters

When building routes with dynamic URL parameters, sometimes you might want to provide a default value if a parameter is not included in the URL. This ensures that your application can still work even if the user doesn’t provide a specific value.

For example, let’s say we’re building a profile page. If the user doesn’t specify a username in the URL, we want to show them as a “Guest” by default. Here’s how we can do this:

To provide default values for parameters, you can use the defaults argument with the @app.route() decorator.

# Route with default parameter
@app.route('/profile', defaults={'username': 'Guest'})
@app.route('/profile/<string:username>')
def profile(username):
    return f"Hello, {username}! Welcome to your profile."

The first route /profile is set up with a default value for the username parameter.

The second route /profile/<string:username> is the normal route where a specific username is passed in the URL.

Output:

Redirects and Error Handling

Okay, readers, we have learned how to set up routes and handle dynamic URLs.

Now, let’s look at another important part of web development—redirects and error handling. As your application grows, you’ll want to manage where users are sent and how errors are shown. This section will cover how to redirect users to different pages and create custom error pages, like a 404 page, when something goes wrong.

Using redirect() to Forward Users

In web applications, there are times when you might need to redirect a user from one page to another. This is where Flask’s redirect() function comes in handy.

The redirect() function in Flask is used to send a user to a different URL. You can use it when a user requests a page, and you want to forward them to another page. Let’s see how this works in our Personal Blog Application.

For instance, after a user posts a new blog entry, we may want to redirect them to the homepage where all blog posts are listed. Let’s demonstrate this:

from flask import Flask, redirect

app = Flask(__name__)

# List of blog posts for example
blog_posts = ['Post 1', 'Post 2', 'Post 3']

# Homepage displaying blog posts
@app.route('/')
@app.route('/dashboard')
@app.route('/home', strict_slashes=True)
def home():
    # Render blog homepage with a link to add a new post
    return f'''
     Blog Homepage 
    <p><a href="/new-post">Create a new post</a></p>
    <ul>
        {''.join([f'<li>{post}</li>' for post in blog_posts])}
    </ul>
    '''

# Route for creating a new blog post
@app.route('/new-post', methods=['GET', 'POST'])
def new_post():
    # Simulate adding a new post (e.g., after submitting a form)
    blog_posts.append("New Post")  # Add a new post to the list

    # Redirect the user back to the homepage to see the updated post list
    return redirect('/')

In this code, the new_post() function simulates a blog post submission by appending “New Post” to the blog_posts list. After the “new post” is added, the redirect() function sends the user back to the homepage, where the updated list of posts is shown.

Output:

Screenshot of a Flask application demonstrating URL redirection. The add new post page redirects to home page within the application.

Redirects with Custom Status Codes

When redirecting users in Flask, you can specify custom status codes to indicate the nature of the redirect. By default, Flask uses a 302 Found status code for redirects, which tells the browser that the redirect is temporary. However, there are situations where a different status code might be more appropriate.

For example:

307 Temporary Redirect: This is used when the redirect is temporary but the browser should resend the exact same HTTP method and data.

301 Moved Permanently: This status code is used when a resource has permanently moved to a new URL. It tells browsers and search engines to update their records for the old URL.

Let’s explore how to use custom status codes with our Personal Blog Application.

Imagine you’ve been running your blog for a while, and you decide to organize it better by changing some URLs. Previously, your blog posts were accessible using a route like /post/<id>, but now you want to organize them under /blog/<id>. To ensure users (and search engines) are redirected properly, you can use a 301 Moved Permanently status code for the old URLs.

# Route for new blog URL
@app.route('/blog/<int:id>')
def blog_post(id):
    return f"This is the new blog post page for post #{id}!"

# A simple route to display a post based on its ID
@app.route('/post/<int:id>')
def show_post(id):
      return redirect(f"/blog/{id}", code=301)

If a user visits /post/5, they’ll be permanently redirected to /blog/5 using a 301 Moved Permanently status code.

Output:

Creating Custom 404 Pages

When users visit a page on your website that doesn’t exist, they typically see a plain, boring “404 Not Found” error. Instead of showing this default message, you can create a custom 404 page that is both informative and aligns with the design of your website.

Custom 404 pages help improve user experience by making the error message more helpful or even fun.

For example, in our Personal Blog Application, we can guide users back to the homepage or provide links to popular blog posts when they encounter a 404 error.

To handle 404 errors in Flask, we use the @app.errorhandler(404) decorator.

# A simple route to display a post based on its ID
@app.route('/post/<int:id>')
def show_post(id):
    # Simulating a case where the requested blog post does not exist
    if  id != 1:  # Let's assume only post with ID 1 exists
        return render_template('404.html'), 404
    return redirect(f"/blog/{id}", code=301)

We use render_template() to load a custom HTML page (404.html) and return it along with the 404 status code. We will learn more about it in next section.

Now, let’s create a custom HTML file for our 404 page. Save the following code in a file named 404.html inside a templates folder.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Page Not Found</title>
    <style>
        body {
            font-family: Arial, sans-serif;
            text-align: center;
            padding: 50px;
            background-color: #f8f9fa;
            color: #333;
        }
        h1 {
            font-size: 50px;
            color: #e63946;
        }
        p {
            font-size: 20px;
            margin: 20px 0;
        }
        a {
            color: #1d3557;
            text-decoration: none;
            font-weight: bold;
        }
        a:hover {
            text-decoration: underline;
        }
    </style>
</head>
<body>
    <h1>404 - Page Not Found</h1>
    <p>Oops! It seems like the page you’re looking for doesn’t exist.</p>
    <p><a href="/">Go back to the homepage</a> or check out our latest blog posts.</p>
</body>
</html>

If a user tries to access /post/2 (or any non-existent route), Flask will trigger the custom 404 error handler. After that, the user will see the 404.html page with a friendly message and a link back to the homepage.

Output:

Flask Application Custom 404 Error Page

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *