54: Flask Templates - MantsSk/CA_PTUA14 GitHub Wiki

Using HTML Templates with Flask

In this lesson, minimal knowledge of HTML and CSS is required. If you are not familiar with them, you can review the material here:

Templates - What are They?

Templates are files that describe HTML content with special variables that will be replaced with real data during the application's operation. These variables can be dynamically included in HTML content, thus allowing the display of different content based on user needs or events.

How to use templates with Flask?

Flask uses the Jinja2 template engine, which allows for easy and efficient manipulation of template content. The main step is to create a templates folder (usually named templates), where all templates are stored.

For example, a template may look like this:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Pavyzdinis šablonas</title>
</head>
<body>
    <h1>Sveiki, {{ vardas }}!</h1>
    <p>Šiame puslapyje galite pamatyti pavyzdinį Flask šabloną.</p>
</body>
</html>

How to use templates in Flask?

To use this template with Flask, you'll need to render it using the render_template function. First, add a "templates" folder next to your app.py file:

Create an HTML file inside it (you can name it index.html) and place the sample template provided above. The structure should look something like this:

image

index.html file

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Pavyzdinis šablonas</title>
</head>
<body>
    <h1>Sveiki, {{ name }}!</h1>
    <p>Šiame puslapyje galite pamatyti pavyzdinį Flask šabloną.</p>
</body>
</html>

Now let's go back to app.py and present this template using render_template. The main purpose of the render_template function is to open the specified HTML template and replace all the variables or data specified in the template with the provided values. In this case, we have the following code:

from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html', name='Jonas')

if __name__ == '__main__':
    app.run(debug=True)

If we run the website, we will get the following result:

image

How to pass and iterate over Python data structures in a template? In our template, we can pass more complex data structures. For example:

posts = [
    {
        'date': '2011-11-11',
        'author': 'Author1',
        'title': 'Title1',
        'content': 'lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum lorem ipsum'
    },
    {
        'date': '2012-10-11',
        'author': 'Author2',
        'title': 'Title2',
        'content': 'test test test test test test test test test test test test test test'
    },
    {
        'date': '2022-05-25',
        'author': 'Author3',
        'title': 'Title3',
        'content': 'content content content content content content content content content content'
    }
]

@app.route('/')
def index():
    return render_template('index.html', name='Jonas', posts=posts)
<body>
    <h1>Posts</h1>
    {{ posts }}  
</body>

We will see a result like this:

image

Here is a rough passing of a dictionary list, and we can see it. To display the list comprehensively, we need to iterate through each of its objects and display their values. We can iterate using a for loop. To use a for loop, its code needs to be written between {% %}.

{% for post in posts %}
    <h2>{{ post.title }}</h2>
    <p>{{ post.date }}, {{ post.author }}</p>
    <hr>
    <p>{{ post.content }}</p>
    <hr>    
{% endfor %}

Now we see a much nicer and tidier result:

image

Template Inheritance

Base template

Template inheritance allows you to create a base template that contains common elements shared across multiple pages, such as header, footer, and navigation menu. You can then create child templates that inherit from the base template and override specific sections as required.

Create a base template containing the common elements:

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}My Website{% endblock %}</title>
</head>
<body>
    <header>
        <h1>Header Content</h1>
        <nav>
            <ul>
                <li><a href="#">Home</a></li>
                <li><a href="#">Profile</a></li>
            </ul>
        </nav>
    </header>
    <main>
        {% block content %}{% endblock %}
    </main>
    <footer>
        <p>&copy; 2024 My Website</p>
    </footer>
</body>
</html>

In the base template (e.g., base.html), you use {% block %} statements to define sections where content can be inserted or overridden by child templates. For example, in base.html, we have the following block defined for the main content:

<main>
    {% block content %}{% endblock %}
</main>

Here, {% block content %} defines a block named "content" where child templates can inject their own content.

Child templates

In the child templates (e.g., index.html, profile.html), you use the {% extends %} statement to inherit from the base template. This tells Flask to use the base template as the starting point and then override specific blocks as needed.

Create child templates that extend the base template and override specific sections. Let's keep the posts that we used before in index.html:

{% extends 'base.html' %}
{% block title %}Home Page{% endblock %}
{% block content %}
<article>
    <h2>Article Title</h2>
    <p>Article content goes here...</p>
</article>
<section>
    <h2>Section Title</h2>
    <p>Section content goes here...</p>
</section>
{% endblock %}

Within the child templates, you can override specific blocks defined in the base template by providing new content inside matching {% block %} statements. For example, in index.html, we override the "title" block to set a custom page title:

{% block title %}Home Page{% endblock %}

Similarly, we override the "content" block to insert specific content for the home page:

{% block content %}
<article>
    <h2>Article Title</h2>
    <p>Article content goes here...</p>
</article>
<section>
    <h2>Section Title</h2>
    <p>Section content goes here...</p>
</section>
{% endblock %}

Let's add another template for our profile:

{% extends 'base.html' %}
{% block title %}Profile Page{% endblock %}
{% block content %}
<article>
    <h2>Profile Information</h2>
    <p>Profile content goes here...</p>
</article>
{% endblock %}

Don’t forget to modify the route in app.py accordingly:

@app.route('/profile/<name>')
def profile(name) -> Response:
    return render_template('profile.html', name='Jonas')

So now when you open the main page (example - http://127.0.0.1:5000/), you will see the main content with posts:

image

And when you open the profile page (example - http://127.0.0.1:5000/profile/mantas), you will see profile info:

image

As you see, both pages have header content inherited from base.html.

The last thing we can modify before the ending of this lesson is the menu in our base template. Currently, it has no links and leads nowhere when clicked:

<li><a href="#">Home</a></li>
<li><a href="#">Profile</a></li>

The modified version will look like this:

<nav>
    <ul>
        <li><a href="{{ url_for('index') }}">Home</a></li>
        <li><a href="{{ url_for('profile', name='Mantas') }}">Profile</a></li>
    </ul>
</nav>

url_for('index') generates the URL for the index route, which is mapped to the main page ("/").

url_for('profile', name='Mantas') generates the URL for the profile endpoint with the name parameter set to "Mantas". This will generate a URL like "/profile/Mantas".

Using url_for allows your Flask application to be more flexible and maintainable, as it automatically adjusts URLs if the routing structure changes.

Static folder and loading CSS files

To load your CSS, you need to move the CSS file to the static directory, as static files like CSS should not be placed within the templates folder. Once the CSS file is in the static directory, you can use url_for to generate the URL for loading it into your templates. If static folder does not exist, create it.

Here are the step by step instructions:

  • Move styles.css to the static directory: Ensure that styles.css is located in the static directory of your Flask application. If it does not exist, create it
  • Link styles.css in your HTML template (base.html) using url_for, example would be like this: <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='styles.css') }}">

The code above generates the URL for the static file styles.css located in the static directory of your application.

The result should look like this:

image

base.html head section will look something like this:

<head>
    <meta charset="UTF-8">
    <title>{% block title %}My Website{% endblock %}</title>
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='index.css') }}">
</head>

Summary

In this lesson, we explored the fundamentals of using HTML templates with Flask. We started by understanding the concept of templates and how they allow for dynamic content rendering in web applications. Flask utilizes the Jinja2 template engine, which facilitates the manipulation of template content efficiently.

Additionally, we explored template inheritance, a powerful feature that enables the creation of base templates containing common elements shared across multiple pages.

Exercise:

Exercise 1:

  • Create a Flask application with a route that renders a basic HTML template.
  • The template should display a simple greeting message, e.g., "Hello, World!".
  • Use the render_template function to render the HTML template.
  • Modify the template to display the passed variable dynamically, e.g., "Hello, {{ name }}!".
  • Test by rendering the template with different variable values.

Exercise 2:

Can be used with content from previous exercise:

  • Define a list of dictionaries containing information about multiple posts, including date, author, title, and content.
  • Pass this list to a template and iterate over each post to display its details.
  • Ensure proper formatting of dates and content in the template.

Exercise 3:

Can be used with content from previous exercise:

  • Create a base HTML template containing common elements like header, footer, and navigation menu.
  • Create child templates for different pages (e.g., home page, profile page) that inherit from the base template.
  • Override specific sections (e.g., title, content) in the child templates as needed.
  • Test by rendering different child templates and ensure that common elements are inherited correctly.

Exercise 4:

Re-create the portfolio website using Flask and Templates. Use inheritance. Make it use template inheritance so it wouldn't use the same code in different files - https://github.com/MantsSk/CA_PTUA10/tree/master/PortfolioMultiPageFIXITPLS

⚠️ **GitHub.com Fallback** ⚠️