5.4. Templating#

As we’ve seen in previous examples, it’s possible to return HTML code directly from a route function, but this approach quickly becomes difficult to manage. Imagine writing the same HTML structure repeatedly, or trying to change the layout of multiple pages - it can become a real headache! Instead of writing HTML inside our Python code, Flask allows us to use templates.

Templates make it possible to separate the HTML structure from the logic in our Python code. Flask uses the Jinja2 templating engine, which allows us to create dynamic HTML pages by inserting Python-like logic (loops, variables, etc.) into the HTML.

5.4.1. Jinja#

Jinja is a powerful templating language for Python. It allows you to embed variables, use control structures like loops, and create reusable layouts. Here’s an example of how Jinja works:

1from jinja2 import Template
2
3# Create a Jinja2 template
4template = Template('Hello, {{ name }}!')
5
6# Render the template with a value for "name"
7rendered = template.render(name='John')
8print(rendered)

Explanation:

  • Line 4: A template is created with a placeholder for the name variable.

  • Line 7: The template is rendered with the value "John" passed to name, and it outputs: "Hello, John!".

5.4.2. Jinja Syntax#

The Template Designer Documentation provided by Jinja outlines the syntax available inside Jinja templates including:

  • variables

  • filters

  • loops

5.4.3. Templates in Flask#

Flask looks for template files inside a folder called templates in your project. Instead of returning HTML directly, you can load and render templates using the render_template() function.

Complete Example

Let’s see how we can use templates in the movie reviews website to display the list of movies.

Project structure:

├── app.py
├── movies.db
└── templates
    └── index.html
app.py#
 1from flask import Flask, render_template
 2from sqlalchemy import create_engine, text
 3
 4app = Flask(__name__)
 5
 6# Connect to the database
 7engine = create_engine('sqlite:///movies.db')
 8
 9@app.route('/')
10def home():
11    # SQL query to select all movies
12    query = text("SELECT * FROM reviews")
13    result = engine.execute(query).fetchall()
14
15    # Render the template and pass the result
16    return render_template('index.html', movies=result)
17
18app.run(debug=True)

Explanation:

  • render_template() is used to:

    • load the index.html file from the templates` folder,

    • pass the query result to the template engine, named movies inside the template context.

This is the index.html template file inside the templates folder:

index.html#
 1<!DOCTYPE html>
 2<html lang="en">
 3    <head>
 4        <title>Movie Reviews</title>
 5    </head>
 6    <body>
 7        <h1>Movie Reviews</h1>
 8        <ul>
 9            {% for movie in movies %}
10                <li>{{ movie[1] }} ({{ movie[2] }}) - Score: {{ movie[5] }}</li>
11            {% endfor %}
12        </ul>
13    </body>
14</html>

Explanation:

  • Lines 9-11: The for loop iterates over each movie and displays its title, year, and score using Jinja2 syntax.

5.4.4. Extending Templates#

Flask templates can be extended to create a base layout that other pages can inherit. This is useful when you have common elements like headers or footers across multiple pages.

index.html#
 1{% extends 'base.html' %}
 2
 3{% block title %}Home - Movie Reviews{% endblock %}
 4
 5{% block content %}
 6    <ul>
 7        {% for movie in movies %}
 8            <li>{{ movie[1] }} ({{ movie[2] }}) - Score: {{ movie[5] }}</li>
 9        {% endfor %}
10    </ul>
11{% endblock %}

Explanation:

  • {% extends 'base.html' %} makes index.html inherit the layout from base.html.

  • {% block title %} overrides the title from the base template.

  • {% block content %} is overridden to display the list of movies.

base.html#
 1<!DOCTYPE html>
 2<html lang="en">
 3    <head>
 4        <title>{% block title %}Movie Reviews{% endblock %}</title>
 5    </head>
 6    <body>
 7        <header>
 8            <h1>Welcome to the Movie Reviews Website</h1>
 9        </header>
10
11        <div class="content">
12            {% block content %}{% endblock %}
13        </div>
14    </body>
15</html>

Explanation:

  • {% block title %} and {% block content %} are placeholders that child templates can override.

5.4.5. Static Files in Templates#

As we saw previously in Serving Static Files, Flask serves static files like CSS, JavaScript, or images from a folder called static.

We manually specified the path to a stylesheet. For example:

<link rel="stylesheet" type="text/css" href="/static/css/styles.css">

However this path will change if we change static_url_path when we create the Flask object. To make sure that we correctly reference the path to static files we can use the url_for template function.

Here’s a simple example:

<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">

Complete Example

Project structure:

├── app.py
├── movies.db
├── static
│   └── style.css
└── templates
    ├── base.html
    └── index.html
base.html#
 1<!DOCTYPE html>
 2<html lang="en">
 3    <head>
 4        <title>{% block title %}Movie Reviews{% endblock %}</title>
 5        <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
 6    </head>
 7    <body>
 8        <header>
 9            <h1>Welcome to the Movie Reviews Website</h1>
10        </header>
11
12        <div class="content">
13            {% block content %}{% endblock %}
14        </div>
15    </body>
16</html>

Explanation:

  • Line 5: {{ url_for('static', filename='style.css') }} generates the correct URL to the style.css file.

  • Now, the custom styles from style.css will be applied to all pages that use the base.html layout.

style.css#
 1    body {
 2        font-family: Arial, sans-serif;
 3        background-color: #f0f0f0;
 4    }
 5
 6    h1 {
 7        color: #333;
 8    }
 9
10    ul {
11        list-style-type: none;
12    }
13
14    li {
15        margin-bottom: 10px;
16    }

5.4.6. Glossary#

Render#

TODO