0
# Templates
1
2
Flask uses Jinja2 as its template engine, providing powerful template rendering capabilities with template inheritance, macros, filters, and automatic escaping for security.
3
4
## Capabilities
5
6
### Template Rendering Functions
7
8
Functions for rendering templates with context data.
9
10
```python { .api }
11
def render_template(template_name_or_list: str | list[str], **context) -> str:
12
"""
13
Render a template with the given context.
14
15
Args:
16
template_name_or_list: Template filename or list of template names
17
**context: Template variables
18
19
Returns:
20
Rendered template as string
21
22
Raises:
23
TemplateNotFound: If template file doesn't exist
24
"""
25
26
def render_template_string(source: str, **context) -> str:
27
"""
28
Render a template from a string source.
29
30
Args:
31
source: Template source code as string
32
**context: Template variables
33
34
Returns:
35
Rendered template as string
36
"""
37
38
def stream_template(template_name_or_list: str | list[str], **context) -> Iterator[str]:
39
"""
40
Stream a template as an iterator of strings.
41
42
Args:
43
template_name_or_list: Template filename or list of template names
44
**context: Template variables
45
46
Yields:
47
Template chunks as strings
48
"""
49
50
def stream_template_string(source: str, **context) -> Iterator[str]:
51
"""
52
Stream a template from string source as an iterator.
53
54
Args:
55
source: Template source code as string
56
**context: Template variables
57
58
Yields:
59
Template chunks as strings
60
"""
61
```
62
63
### Template Context Processing
64
65
Decorators and methods for adding variables to template context.
66
67
```python { .api }
68
def context_processor(self, f: Callable) -> Callable:
69
"""
70
Decorator to register a template context processor.
71
72
Args:
73
f: Function that returns dict of context variables
74
75
Returns:
76
The original function
77
"""
78
79
def update_template_context(self, context: dict[str, Any]) -> None:
80
"""
81
Update template context with additional variables.
82
83
Args:
84
context: Context dictionary to update
85
"""
86
```
87
88
### Template Filters
89
90
Functions for registering custom template filters.
91
92
```python { .api }
93
def template_filter(self, name: str | None = None) -> Callable:
94
"""
95
Decorator to register a template filter.
96
97
Args:
98
name: Filter name (defaults to function name)
99
100
Returns:
101
Decorator function
102
"""
103
104
def add_template_filter(self, f: Callable, name: str | None = None) -> None:
105
"""
106
Register a template filter function.
107
108
Args:
109
f: Filter function
110
name: Filter name (defaults to function name)
111
"""
112
```
113
114
### Template Tests
115
116
Functions for registering custom template tests.
117
118
```python { .api }
119
def template_test(self, name: str | None = None) -> Callable:
120
"""
121
Decorator to register a template test.
122
123
Args:
124
name: Test name (defaults to function name)
125
126
Returns:
127
Decorator function
128
"""
129
130
def add_template_test(self, f: Callable, name: str | None = None) -> None:
131
"""
132
Register a template test function.
133
134
Args:
135
f: Test function
136
name: Test name (defaults to function name)
137
"""
138
```
139
140
### Template Globals
141
142
Functions for registering global template functions.
143
144
```python { .api }
145
def template_global(self, name: str | None = None) -> Callable:
146
"""
147
Decorator to register a template global function.
148
149
Args:
150
name: Global name (defaults to function name)
151
152
Returns:
153
Decorator function
154
"""
155
156
def add_template_global(self, f: Callable, name: str | None = None) -> None:
157
"""
158
Register a template global function.
159
160
Args:
161
f: Global function
162
name: Global name (defaults to function name)
163
"""
164
```
165
166
### Template Environment
167
168
Access to the Jinja2 environment for advanced customization.
169
170
```python { .api }
171
class Flask:
172
jinja_env: Environment # Jinja2 environment instance
173
174
def create_jinja_environment(self) -> Environment:
175
"""
176
Create the Jinja2 environment.
177
178
Returns:
179
Configured Jinja2 environment
180
"""
181
182
def select_jinja_autoescape(self, filename: str) -> bool:
183
"""
184
Determine if autoescape should be enabled for a template.
185
186
Args:
187
filename: Template filename
188
189
Returns:
190
True if autoescape should be enabled
191
"""
192
```
193
194
## Usage Examples
195
196
### Basic Template Rendering
197
198
```python
199
from flask import Flask, render_template
200
201
app = Flask(__name__)
202
203
@app.route('/')
204
def home():
205
return render_template('index.html', title='Home Page')
206
207
@app.route('/user/<name>')
208
def user_profile(name):
209
user_data = {
210
'name': name,
211
'email': f'{name}@example.com',
212
'joined': '2023-01-01'
213
}
214
return render_template('user.html', user=user_data)
215
216
@app.route('/posts')
217
def posts():
218
posts = [
219
{'title': 'First Post', 'content': 'Hello World'},
220
{'title': 'Second Post', 'content': 'Flask is great'}
221
]
222
return render_template('posts.html', posts=posts)
223
```
224
225
### Template with Context Variables
226
227
```python
228
from flask import Flask, render_template
229
from datetime import datetime
230
231
app = Flask(__name__)
232
233
@app.route('/dashboard')
234
def dashboard():
235
context = {
236
'user': {
237
'name': 'John Doe',
238
'role': 'admin',
239
'last_login': datetime.now()
240
},
241
'stats': {
242
'total_users': 1250,
243
'active_sessions': 45,
244
'system_status': 'healthy'
245
},
246
'navigation': [
247
{'name': 'Dashboard', 'url': '/dashboard'},
248
{'name': 'Users', 'url': '/users'},
249
{'name': 'Settings', 'url': '/settings'}
250
]
251
}
252
253
return render_template('dashboard.html', **context)
254
```
255
256
### Template Inheritance Example
257
258
Base template (`templates/base.html`):
259
```html
260
<!DOCTYPE html>
261
<html>
262
<head>
263
<title>{% block title %}{% endblock %} - My App</title>
264
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
265
</head>
266
<body>
267
<header>
268
<nav>
269
<a href="{{ url_for('home') }}">Home</a>
270
<a href="{{ url_for('about') }}">About</a>
271
</nav>
272
</header>
273
274
<main>
275
{% block content %}{% endblock %}
276
</main>
277
278
<footer>
279
<p>© 2023 My App</p>
280
</footer>
281
</body>
282
</html>
283
```
284
285
Child template (`templates/page.html`):
286
```html
287
{% extends "base.html" %}
288
289
{% block title %}{{ page_title }}{% endblock %}
290
291
{% block content %}
292
<h1>{{ page_title }}</h1>
293
<p>{{ page_content }}</p>
294
{% endblock %}
295
```
296
297
### Custom Template Filters
298
299
```python
300
from flask import Flask, render_template
301
from datetime import datetime
302
303
app = Flask(__name__)
304
305
@app.template_filter('dateformat')
306
def dateformat_filter(value, format='%Y-%m-%d'):
307
"""Format a datetime object."""
308
if value is None:
309
return ''
310
return value.strftime(format)
311
312
@app.template_filter('currency')
313
def currency_filter(value):
314
"""Format a number as currency."""
315
return f'${value:,.2f}'
316
317
# Alternative registration method
318
def reverse_filter(s):
319
"""Reverse a string."""
320
return s[::-1]
321
322
app.add_template_filter(reverse_filter, 'reverse')
323
324
@app.route('/formatted')
325
def formatted_data():
326
data = {
327
'date': datetime.now(),
328
'price': 1234.56,
329
'text': 'Hello World'
330
}
331
return render_template('formatted.html', **data)
332
```
333
334
Template using filters (`templates/formatted.html`):
335
```html
336
<p>Date: {{ date | dateformat('%B %d, %Y') }}</p>
337
<p>Price: {{ price | currency }}</p>
338
<p>Reversed: {{ text | reverse }}</p>
339
```
340
341
### Custom Template Tests
342
343
```python
344
from flask import Flask, render_template
345
346
app = Flask(__name__)
347
348
@app.template_test('even')
349
def is_even(n):
350
"""Test if a number is even."""
351
return n % 2 == 0
352
353
@app.template_test('admin')
354
def is_admin(user):
355
"""Test if user is admin."""
356
return user.get('role') == 'admin'
357
358
@app.route('/users')
359
def users():
360
users = [
361
{'name': 'John', 'role': 'admin', 'id': 1},
362
{'name': 'Jane', 'role': 'user', 'id': 2},
363
{'name': 'Bob', 'role': 'user', 'id': 3}
364
]
365
return render_template('users.html', users=users)
366
```
367
368
Template using tests (`templates/users.html`):
369
```html
370
{% for user in users %}
371
<div class="user {% if user.id is even %}even{% else %}odd{% endif %}">
372
{{ user.name }}
373
{% if user is admin %}
374
<span class="badge">Admin</span>
375
{% endif %}
376
</div>
377
{% endfor %}
378
```
379
380
### Template Globals
381
382
```python
383
from flask import Flask, render_template, url_for
384
import os
385
386
app = Flask(__name__)
387
388
@app.template_global('get_version')
389
def get_version():
390
"""Get application version."""
391
return os.environ.get('APP_VERSION', '1.0.0')
392
393
@app.template_global('active_page')
394
def active_page(page):
395
"""Check if current page is active."""
396
from flask import request
397
return 'active' if request.endpoint == page else ''
398
399
@app.route('/about')
400
def about():
401
return render_template('about.html')
402
```
403
404
Template using globals (`templates/about.html`):
405
```html
406
<p>Version: {{ get_version() }}</p>
407
<nav>
408
<a href="{{ url_for('home') }}" class="{{ active_page('home') }}">Home</a>
409
<a href="{{ url_for('about') }}" class="{{ active_page('about') }}">About</a>
410
</nav>
411
```
412
413
### Context Processors
414
415
```python
416
from flask import Flask, render_template, g
417
from datetime import datetime
418
419
app = Flask(__name__)
420
421
@app.context_processor
422
def inject_globals():
423
"""Inject global variables into all templates."""
424
return {
425
'now': datetime.now(),
426
'app_name': 'My Flask App',
427
'year': datetime.now().year
428
}
429
430
@app.context_processor
431
def inject_user():
432
"""Inject current user into templates."""
433
# In real app, get from session or database
434
user = getattr(g, 'user', None)
435
return {'current_user': user}
436
437
@app.before_request
438
def load_user():
439
# Simulate loading user
440
g.user = {'name': 'John Doe', 'role': 'user'}
441
442
@app.route('/profile')
443
def profile():
444
return render_template('profile.html')
445
```
446
447
Template using context variables (`templates/profile.html`):
448
```html
449
<h1>{{ app_name }}</h1>
450
<p>Welcome, {{ current_user.name }}!</p>
451
<p>Current time: {{ now.strftime('%Y-%m-%d %H:%M:%S') }}</p>
452
<footer>© {{ year }} {{ app_name }}</footer>
453
```
454
455
### Streaming Templates
456
457
```python
458
from flask import Flask, stream_template
459
import time
460
461
app = Flask(__name__)
462
463
@app.route('/stream')
464
def stream_data():
465
def generate_data():
466
"""Generate data for streaming template."""
467
for i in range(10):
468
yield {'index': i, 'data': f'Item {i}'}
469
time.sleep(0.5) # Simulate processing time
470
471
return stream_template('stream.html', items=generate_data())
472
473
@app.route('/large-report')
474
def large_report():
475
# Stream large dataset
476
def get_report_data():
477
# Simulate large dataset
478
for i in range(1000):
479
yield {
480
'id': i,
481
'name': f'Record {i}',
482
'value': i * 10
483
}
484
485
return stream_template('report.html', records=get_report_data())
486
```
487
488
Streaming template (`templates/stream.html`):
489
```html
490
<!DOCTYPE html>
491
<html>
492
<head>
493
<title>Streaming Data</title>
494
</head>
495
<body>
496
<h1>Live Data Stream</h1>
497
<ul>
498
{% for item in items %}
499
<li>{{ item.index }}: {{ item.data }}</li>
500
{% if loop.index % 5 == 0 %}
501
<!-- Flush every 5 items -->
502
{% endif %}
503
{% endfor %}
504
</ul>
505
</body>
506
</html>
507
```
508
509
### Template Security and Escaping
510
511
```python
512
from flask import Flask, render_template_string, Markup
513
514
app = Flask(__name__)
515
516
@app.route('/safe')
517
def safe_content():
518
user_input = '<script>alert("XSS")</script>'
519
safe_html = '<strong>Safe HTML</strong>'
520
521
template = '''
522
<p>User input (escaped): {{ user_input }}</p>
523
<p>Safe HTML (marked safe): {{ safe_html | safe }}</p>
524
<p>Or use Markup: {{ markup_html }}</p>
525
'''
526
527
return render_template_string(
528
template,
529
user_input=user_input,
530
safe_html=safe_html,
531
markup_html=Markup(safe_html)
532
)
533
534
@app.template_filter('safe_html')
535
def safe_html_filter(html):
536
"""Custom filter to mark HTML as safe."""
537
# In real app, sanitize HTML first
538
return Markup(html)
539
```