This note is from 8 months ago. You're viewing content from a previous lesson.
At the start of every class, please:
Jinja is a templating language that lets you combine HTML with Python data. Flask uses Jinja to fill in placeholders in your HTML so your pages can display dynamic content (e.g., a user’s name, a list of items, a table of results).
Define what a template is and where Flask looks for templates.
Construct a Flask view that passes variables to a Jinja template.
Apply Jinja syntax for variables {{ ... }}, conditionals {% if ... %}, and loops {% for ... %}.
Use common Jinja filters (e.g., |upper, |length).
Organize templates with basic inheritance (base.html and {% block %}).
Create a templates/ folder in the same directory as app.py.
your-project/
├─ app.py
└─ templates/
├─ base.html
├─ hello.html
└─ items.html
Flask automatically looks in templates/ for HTML files.
app.pyfrom flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def home():
# Python data you want to display in HTML:
name = "Ada Lovelace"
# Pass it to the template as a named variable:
return render_template("hello.html", person=name)
if __name__ == "__main__":
app.run(debug=True)
templates/hello.html<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>Hello</title></head>
<body>
<h1>Hello, {{ person }}!</h1>
<p>This value came from Python through Flask → Jinja.</p>
</body>
</html>
Start the server: python app.py
Visit: http://127.0.0.1:5000/
Expected output: “Hello, Ada Lovelace!”
Key syntax:
{{ person }} = print the value of a variable.
Values come from render_template("hello.html", person=name).
app.py with more data@app.route("/items")
def items():
username = "Grace"
cart = ["notebook", "pencil", "eraser"]
is_member = True
return render_template("items.html", username=username, cart=cart, is_member=is_member)
templates/items.html<!doctype html>
<html lang="en">
<head><meta charset="utf-8"><title>Items</title></head>
<body>
<h1>Welcome, {{ username }}.</h1>
{% if is_member %}
<p>Member discount applied.</p>
{% else %}
<p>No discount. <a href="#">Join now</a></p>
{% endif %}
<h2>Your cart ({{ cart|length }} items)</h2>
<ul>
{% for item in cart %}
<li>{{ loop.index }}. {{ item|upper }}</li>
{% endfor %}
</ul>
<p>First item: {{ cart[0] }}</p>
</body>
</html>
New syntax:
{% if ... %} ... {% endif %} = conditional blocks.
{% for x in list %} ... {% endfor %} = loops.
{{ loop.index }} = 1-based index inside a loop.
|upper, |length = filters that transform output before printing.
Goal: Put shared HTML (head, nav, footer) into base.html. Each page fills in a {% block %}.
templates/base.html<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>{% block title %}My Site{% endblock %}</title>
</head>
<body>
<header>
<nav>
<a href="/">Home</a> | <a href="/items">Items</a>
</nav>
<hr>
</header>
<main>
{% block content %}{% endblock %}
</main>
<hr>
<footer>
<small>© 2025 My Site</small>
</footer>
</body>
</html>
hello.html to extend base.html{% extends "base.html" %}
{% block title %}Hello{% endblock %}
{% block content %}
<h1>Hello, {{ person }}!</h1>
<p>Now using template inheritance.</p>
{% endblock %}
items.html to extend base.html{% extends "base.html" %}
{% block title %}Your Items{% endblock %}
{% block content %}
<h1>Welcome, {{ username }}.</h1>
{% if is_member %}
<p>Member discount applied.</p>
{% else %}
<p>No discount. <a href="#">Join now</a></p>
{% endif %}
<h2>Your cart ({{ cart|length }} items)</h2>
<ul>
{% for item in cart %}
<li>{{ loop.index }}. {{ item|upper }}</li>
{% endfor %}
</ul>
{% endblock %}
Print variable: {{ variable }}
If: {% if cond %}...{% elif ... %}...{% else %}...{% endif %}
For: {% for x in seq %}...{{ loop.index }}...{% endfor %}
Filters: {{ name|upper }}, {{ text|lower }}, {{ items|length }}
Default value: {{ value|default("N/A") }}
Inheritance: top file uses {% block %}s; child uses {% extends "base.html" %} then fills blocks.
TemplateNotFound: File not in templates/ or wrong filename.
UndefinedError: You used {{ x }} but didn’t pass x in render_template.
Jinja syntax error: Mismatched tags. Example: started {% if %} but forgot {% endif %}.
Nothing changes on refresh: Your browser cached it. Hard refresh, or restart Flask if you edited Python.
Variables (Apply):
Add age = 17 in / route and show: Hello, Ada Lovelace (17). Use hello.html.
Conditionals (Apply):
In /items, add total = 9.5. Show “Shipping is free” if total >= 10 else “Shipping: 3.00”.
Loop + index (Apply):
Add a list of courses = ["Math", "CS", "History"] and render them as an ordered list with index.
Filter (Apply):
Print username in title case: {{ username|title }}.
Default filter (Apply):
Try {{ nickname|default("no nickname") }} without passing nickname.
Inheritance (Construct):
Add a new page /about that extends base.html and fills both title and content blocks.
Dictionary access: Pass user = {"first": "Grace", "last": "Hopper"} and print {{ user.first }} {{ user.last }}.
Loop over dict: Pass scores = {"Exam": 88, "Project": 95} and render a table of keys/values.
Include partials: Create templates/_message.html and include it with {% include "_message.html" %}.
<!-- templates/_message.html -->
<p><em>This is a reusable partial.</em></p>
Answer briefly:
What is the difference between {{ ... }} and {% ... %} in Jinja?
Why do we use a base.html with {% block %}s?
Show one example of a Jinja filter and explain what it does.
Level 3–4 (Satisfactory): Correctly passes at least one variable and displays it with {{ ... }}; uses one control structure.
Level 5–6 (Good): Uses variables, a loop, a conditional, and at least one filter; clean inheritance with base.html.
Level 7 (Excellent): Adds a new route + template that extends base.html, uses filters and defaulting, and demonstrates clear organization and comments.
Jinja auto-escapes variables in HTML (it converts < to <, etc.). This helps prevent XSS. Only use |safe on trusted content you 100% control.
<!-- Dangerous if the content comes from users -->
{{ html_snippet|safe }}
That’s it. Build the three templates, pass some data from your Flask route, and practice with variables, loops, conditionals, and filters.