Templates

Part 1, Chapter 5


This chapter looks at how to render templates using the Jinja templating engine.

Template Engines

Thus far we've added HTML directly to the return value within the view function:

@app.route('/about')
def about():
    return '<h2>About this application...</h2>'

This is not an ideal solution as it adds clutter to the function. Plus, as your app scales and you add more and more view functions, you'll want to reuse the common pieces of HTML across several views. Templating engines simplify this for you.

With them, you can:

  1. Use static HTML template files to decouple the view from the HTML
  2. Separate the HTML structure from the content
  3. Use basic programming constructs -- variables, conditionals, and for loops -- directly in the HTML to manipulate the final rendered output

Flask comes packaged with the Jinja templating engine out-of-the-box, which we'll use to generate our HTML files.

Again, a template file contains variables and/or expressions, which get replaced with values when a template is rendered:

Jinja Template Processing

The Jinja2 module was already installed when we installed the Flask module:

(venv)$ pip freeze
blinker==1.7.0
click==8.1.7
Flask==3.0.1
itsdangerous==2.1.2
Jinja2==3.1.3      # <-- !!
MarkupSafe==2.1.4
Werkzeug==3.0.1

Jinja is also commonly referred to as "Jinja2" to specify the newest release version. Officially, the documentation refers to the package as 'jinja'.

Project Structure

In this chapter, we'll expand the project structure to include a new "templates" folder for defining each template file:

(venv)$ tree -L 2

.
├── app.py
├── requirements.txt
├── templates
│   ├── about.html
│   ├── base.html
│   └── index.html
└── venv

Go ahead and add the templates folder along with the three HTML files now.

Template Rendering

Within a Flask app, templates get rendered using the render_template() function. This tells Flask to serve up an HTML template via Jinja.

Let's update the index() view function in app.py to render a template when the '/' URL is accessed:

from flask import Flask, render_template


@app.route('/')
def index():
    return render_template('index.html')

Update the template file, templates/index.html:

<h1>Welcome to the Flask Stock Portfolio App!</h1>

If you navigate to the '/' URL, you should see:

Index View with Templates

Template Variables

Update the About page so that it uses a template by first updating the about() function in app.py:

@app.route('/about')
def about():
    return render_template('about.html')

Next, the template file needs to be updated in templates/about.html:

<h1>About</h1>
<br>
<h2>This application is built using the Flask web framework.</h2>

To pass in data to include when the template is rendered, add the company_name argument to the about() function:

@app.route('/about')
def about():
    return render_template('about.html', company_name='TestDriven.io')

The company_name variable can now be used in the template file (templates/about.html):

<h1>About</h1>
<br>
<h2>This application is built using the Flask web framework.</h2>
<br>
<h2>Course developed by {{ company_name }}.</h2>

Navigate to the '/about' URL:

About Page - Variable Passed In

Template Logic

What if the company_name is not specified?

@app.route('/about')
def about():
    # return render_template('about.html', company_name='TestDriven.io')
    return render_template('about.html')

We can use IF-ELSE logic within the template file (about.html) like so:

<h1>About</h1>
<br>
<h2>This application is built using the Flask web framework.</h2>
<br>
{% if company_name %}
  <h2>Course developed by {{ company_name }}.</h2>
{% else %}
  <h2>Enjoy the course!</h2>
{% endif %}

Try it out:

IF-ELSE Block in Template - About

Template Inheritance

The concept of template inheritance is similar to classes in object-oriented design. There's typically a parent template (like base.html) that provides an overall structure to your pages and then child templates (such as about.html) that provide the content of the pages.

Let's look at this in action.

Home Page

To start, update the templates/base.html file:

<!DOCTYPE html>
<html lang="en">
  <head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>Flask Stock Portfolio App</title>
  </head>

  <body>
    <header>
      <h1>Flask Stock Portfolio App</h1>
    </header>

    <main>
      <!-- child template -->
      {% block content %}
      {% endblock %}
    </main>
  </body>
</html>

This parent template will be used by each child template.

The templates/index.html file should be updated to use the base.html file:

{% extends "base.html" %}

{% block content %}
<h1>Welcome to the Flask Stock Portfolio App!</h1>
{% endblock %}

If you navigate to the '/' URL now, you'll now see the page has been updated to include the header:

Index Page - Template Inheritance

About Page

Next, update the templates/about.html file to use the base.html file:

{% extends "base.html" %}

{% block content %}
<h1>About</h1>
<br>
<h2>This application is built using the Flask web framework.</h2>
<br>
{% if company_name %}
  <h2>Course developed by {{ company_name }}.</h2>
{% else %}
  <h2>Enjoy the course!</h2>
{% endif %}
{% endblock %}

If you navigate to the '/' URL now, you'll see the page has been updated to include the header:

About Page - Template Inheritance 1

Conclusion

In this chapter we looked at templating with Jinja. You should now be able to:

  1. Explain what a template engine is
  2. Configure Flask to render templates using Jinja
  3. Use variables and conditional logic in a template
  4. Set up basic template inheritance



Mark as Completed