Passing Variables to Templates
Templates become powerful when you pass data to them.
Passing variables from Flask
Section titled “Passing variables from Flask”from flask import Flask, render_template
app = Flask(__name__)
@app.route("/hello/<name>")
def hello(name):
return render_template("hello.html", name=name)Template templates/hello.html:
<!doctype html>
<html>
<body>
<h1>Hello, {{ name }}!</h1>
</body>
</html>Passing dictionaries and lists
Section titled “Passing dictionaries and lists”@app.route("/users")
def users():
data = [
{"username": "ravi", "role": "admin"},
{"username": "alex", "role": "user"},
]
return render_template("users.html", users=data)In Jinja you can access:
{{ user.username }}or{{ user['username'] }}
Good practice
Section titled “Good practice”- Keep templates focused on presentation.
- Do validation/computation in Python, then pass already-clean data to the template.
This makes templates simpler and safer.
🧪 Try It Yourself
Section titled “🧪 Try It Yourself”Exercise 1 – Create a Flask App
Section titled “Exercise 1 – Create a Flask App”Exercise 2 – Dynamic Route
Section titled “Exercise 2 – Dynamic Route”Exercise 3 – Return JSON
Section titled “Exercise 3 – Return JSON”An undefined variable is not an error
Section titled “An undefined variable is not an error”This is the behaviour that makes template bugs so hard to see. A name that does not exist renders as nothing at all:
flowchart TD
T["{{ name }}"] --> Q{"is 'name' in the context?"}
Q -->|"yes"| P["print its value, escaped"]
Q -->|"no"| U["Undefined"]
U --> R["prints as an EMPTY STRING
no error, no warning"]
U --> B["is falsy in {% if %}"]
U --> A["but attribute access RAISES
UndefinedError"]
Measured:
render_template_string("start[{{ nope }}]end")
# 'start[]end' <- silent
render_template_string("{% if missing %}yes{% else %}no{% endif %}")
# 'no' <- undefined is falsy
render_template_string("{{ nope.attr }}")
# jinja2.exceptions.UndefinedError: 'nope' is undefinedSo a typo in a variable name produces a blank space on the page, while a typo in an attribute produces a loud error. The first is the one that reaches production.
What is already available without passing it
Section titled “What is already available without passing it”Flask injects a few names into every template context:
| name | what it is |
|---|---|
url_for | build a URL from an endpoint name |
request | the current request object |
session | the session dict |
g | the per-request global object |
config | the app config |
return render_template("profile.html", user=user, posts=posts)<h1>{{ user.name }}</h1>
{% for p in posts %}
<a href="{{ url_for('post', id=p.id) }}">{{ p.title }}</a>
{% endfor %}To add your own name to every template without passing it each time, use a context processor:
@app.context_processor
def inject_year():
return {"year": datetime.now().year} # {{ year }} now works everywhereSee it move
Section titled “See it move”Check yourself
Section titled “Check yourself”-
A template contains {{ usr }} but the view passed user=. What is rendered?
Undefined renders as empty. Measured: 'start[{{ nope }}]end' gave 'start[]end'. A typo in a variable name becomes a blank space, which is why it reaches production.
pch.quizShowAnswer
C — an empty string, with no error or warning — Undefined renders as empty. Measured: 'start[{{ nope }}]end' gave 'start[]end'. A typo in a variable name becomes a blank space, which is why it reaches production.
-
Why does {{ nope }} render silently while {{ nope.attr }} raises?
Undefined has a string form (empty) and is falsy, but attribute access on it raises UndefinedError. Set app.jinja_env.undefined = StrictUndefined to make the quiet case loud in development.
pch.quizShowAnswer
B — printing Undefined is defined as empty, but getting an attribute from Undefined is an error — Undefined has a string form (empty) and is falsy, but attribute access on it raises UndefinedError. Set app.jinja_env.undefined = StrictUndefined to make the quiet case loud in development.
-
Which name is available in every template without being passed by the view?
Flask injects url_for, request, session, g and config. Anything else must be passed to render_template or added with a @app.context_processor.
pch.quizShowAnswer
B — url_for — Flask injects url_for, request, session, g and config. Anything else must be passed to render_template or added with a @app.context_processor.
pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading