LEARN PYTHON | Jinja Templates And Static Files

Lesson objective

Render Jinja templates, pass data explicitly, reuse a base layout and link CSS through Flask’s static endpoint while preserving HTML escaping.

Learn

09.2 | JINJA TEMPLATES AND STATIC FILES

01 | WHY A LONG HTML STRING BECOMES AWKWARD

A route returning one short heading is manageable. A full page has a head, navigation, repeated cards and a footer. Building all of that with Python string concatenation mixes presentation with request logic and makes changes harder to read.

A template is a text document containing page structure and placeholders. Flask uses Jinja to render it: combine the template with supplied values and produce the final response.

The browser receives the rendered HTML, not the original Jinja instructions. A for loop in a template runs on the server during rendering, while ordinary JavaScript in the final page runs in the browser.

02 | EXPLORE THE DOWNLOADABLE LEARNING LAB

The learning lab provides the complete route, template and form files used in lessons 09.1 to 09.3. Download and extract it, install requirements.txt in your environment and follow its README to set a local secret before starting the app.

app.py contains the factory and routes. templates contains base.html, home.html, tasks.html and new.html. static contains lab.css. instance holds the generated local SQLite file and is not included in the download.

This first lab has no account access rules, so use fictional records locally. The separate portal later adds sign-in and ownership. Do not treat a practice task form as a publicly secured student-data system.

Download the Flask learning lab
LocationPurposeSent directly to browser?
app.pyRequest and data logicNo
templates/*.htmlServer-side page templatesRendered output only
static/lab.cssStylesheet assetYes
instance/tasks.sqlite3Stored task recordsNo

03 | UNDERSTAND THE THREE JINJA DELIMITERS

{{ ... }} inserts an expression’s value into the output. {% ... %} controls template statements such as loops and conditions. {# ... #} is a template comment omitted from the rendered response.

A name such as tasks must be supplied by the route or template context. It is not automatically every Python variable in your module. render_template("tasks.html", tasks=rows) explicitly provides that data.

Use templates for display decisions, while database queries, access rules and complex validation belong in Python. A page should not need to know how to assemble a SQL query just to show a list.

TEMPLATE | TEMPLATES/TASKS.HTML

{% extends 'base.html' %}{% block title %}Tasks | Flask Learning Lab{% endblock %}{% block content %}<h2>Planned tasks</h2><ul>{% for task in tasks %}<li>{{ task['title'] }}: {{ task['minutes'] }} minutes</li>{% else %}<li>No tasks yet.</li>{% endfor %}</ul>{% endblock %}

04 | A RUNNABLE INLINE DEMONSTRATION

This complete small file uses render_template_string to make one rendering example runnable before creating multiple template files. Save it as template_demo.py and run it through Flask. The lab uses separate files for maintainable page structure.

The route supplies a list of dictionaries. The template loop outputs each task, while its else block handles the empty collection. That else belongs to the loop’s empty-case behaviour, not to a Python condition.

Set records to [] and request the page again to inspect the empty view. The page should explain that no records exist rather than display an unexplained blank area.

from flask import Flask, render_template_string

app = Flask(__name__)

@app.get("/tasks")
def tasks():
    records = [{"title": "Trace loops", "minutes": 25}]
    template = """<h1>Study tasks</h1><ul>
    {% for task in tasks %}
      <li>{{ task['title'] }}: {{ task['minutes'] }} minutes</li>
    {% else %}
      <li>No tasks yet.</li>
    {% endfor %}</ul>"""
    return render_template_string(template, tasks=records)

05 | SHARE ONE BASE LAYOUT

base.html defines the document head, navigation and main region. Child templates extend it and fill named blocks. If you improve the shared navigation, every inheriting page can receive the change.

A block is a replaceable template region. The base defines {% block content %}; the child supplies its own content block. The page title can have a separate block so browser tabs remain informative.

This is server-side inheritance, not CSS inheritance. CSS inheritance controls selected style properties; template inheritance controls generated document structure.

TEMPLATE | BASE LAYOUT EXCERPT

<title>{% block title %}Flask Learning Lab{% endblock %}</title>
<main>{% block content %}{% endblock %}</main>

TEMPLATE | CHILD EXCERPT

{% extends "base.html" %}
{% block title %}Tasks | Flask Learning Lab{% endblock %}
{% block content %}<h2>Your study tasks</h2>{% endblock %}

06 | BUILD URLS FROM ENDPOINTS

url_for uses the endpoint name, normally the view function name, to build a URL. url_for("tasks") describes the destination without duplicating the current route spelling in every template.

For a parameterised route, supply the value: url_for("hello", name="Maya"). Flask constructs the path according to that registered rule. Missing required values or an unknown endpoint are real errors to fix, not reasons to suppress the exception.

This also helps when an application is served under a configured URL prefix. Do not use a filesystem path such as templates/tasks.html as a browser navigation URL. The handler renders that file; the client requests a route.

07 | STATIC ASSETS HAVE THEIR OWN URL

The stylesheet is a static file: Flask can send its bytes without rendering Jinja. Generate its link through url_for("static", filename="lab.css"). The filename is relative to the configured static folder.

A missing stylesheet might be a wrong filename, folder or URL, not a broken template loop. Inspect the browser’s network requests and the server response status to locate the problem.

A web app’s static folder is different from a template source folder. Do not place secrets or private databases inside static, because those files are intended to be served to clients.

TEMPLATE | QUOTED STYLESHEET ATTRIBUTE

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

08 | PREVIEW DATA VERSUS HTML

The model shows a template value as text. An HTML-looking title remains text rather than becoming a new element. The actual learning lab uses Jinja autoescaping; this browser illustration uses textContent.

ILLUSTRATED OUTPUT

09 | KEEP AUTOESCAPING AND CONTEXT IN MIND

Flask enables autoescaping for its normal HTML template use, including the .html files here. Characters that could open tags are escaped when untrusted values are inserted as text. This helps prevent a title becoming executable HTML.

Do not apply |safe to user text merely because you want formatting. That marks the value as trusted markup and bypasses an important protection. If rich text is required, it needs a deliberate sanitisation policy.

Escaping is context-dependent. Quote attribute values; do not assume HTML text escaping makes arbitrary JavaScript, CSS or URL schemes safe. The project displays descriptions as text and avoids user-supplied active URLs and scripts.

10 | TEST THE PAGE, THE EMPTY STATE AND THE ASSET

Check a normal list, an empty list, a title containing an apostrophe and HTML-looking text. Inspect the response source to see escaped characters and the final generated URL.

Use the test client to request /static/lab.css and verify its status and content type. A successful main page does not prove every asset is present.

Try it: change the base title block, add a child heading and verify shared navigation still works. Keep data access in the route and presentation in the template.

PUT YOUR SKILLS TO WORK

Use the Section 9 challenges to practise these ideas independently. Work in your Python editor, predict results and keep a short test record.

Section 9 challenges Python course navigation

MATCH THE PYTHON TERM TO ITS MEANING

Terminology

Terminology

Template

A document combining fixed structure with supplied values and instructions.

Jinja

The template engine used by Flask.

Render

Generate output from a template and context.

Context

The values available during template rendering.

Expression delimiter

Double braces inserting a value into output.

Statement delimiter

Brace-percent markers controlling template logic.

Template comment

Brace-hash content omitted from output.

Inheritance

Reusing a base template and filling named regions.

Block

A replaceable region of an inherited template.

Endpoint

The named destination used by Flask URL building.

url_for

A function generating a URL for an endpoint.

Static asset

A client-served file such as CSS or an image.

Autoescaping

Escaping inserted values automatically under the template policy.

Trusted markup

Content deliberately allowed to behave as HTML.

Empty state

A useful display when a collection has no records.

Attribute

A named HTML element option whose value should be quoted.

Presentation

The interface structure and appearance shown to the user.

Separation of concerns

Giving different responsibilities clear code boundaries.

Questions

Questions

CHECK YOUR UNDERSTANDING

Select all correct choices. Each exact set earns one point.

1. Which template statements are correct?
2. What does {{ task.title }} do?
3. What is {% for task in tasks %} used for?
4. Which files belong in static in this project?
5. Why call url_for?
6. What does template inheritance share?
7. Which output choices are suitable for an untrusted project title?
8. An empty tasks list reaches the for-else template. What is shown?

EXPLAIN AND COMPARE

Write your answers before revealing each sample.

1. Explain how a Python list becomes a task page.

2. Describe base and child templates.

3. Explain the empty-list branch.

4. Distinguish templates and static files.

5. Explain default escaping with a title containing <strong>.

6. Explain why |safe is inappropriate for an ordinary user title.

7. Describe the benefit of url_for.

8. Give two tests for a task template.

CODE IT | YOUR INDEPENDENT CHALLENGE

Render a small collection through an inherited template and test empty data and markup in a title.

Work in your Python editor. Keep a working version, explain one design choice and record at least two tests. Use the Learn tab for worked examples before comparing your result.

Flashcards

Flashcards

Click to flip. Select the ideas you need to revisit.

0 cards selected for revision.

    Selections are kept while this page is open.

    Workbook

    Workbook

    COMING SOON

    The workbook for Jinja Templates And Static Files is coming soon. Complete the matching task and written questions in your exercise book.