0
# Template Fragment Caching
1
2
Jinja2 template integration for caching expensive template fragments. This feature allows caching portions of templates to improve rendering performance, especially useful for complex layouts, database-driven content, or expensive template operations.
3
4
## Capabilities
5
6
### Jinja2 Cache Extension
7
8
Flask-Caching automatically registers a Jinja2 extension that provides template-level caching control through the `{% cache %}` template tag.
9
10
```python { .api }
11
class CacheExtension(Extension):
12
"""
13
Jinja2 extension for template fragment caching.
14
Automatically registered when Cache is initialized with with_jinja2_ext=True.
15
"""
16
17
# Template syntax
18
"""
19
{% cache timeout key1[, key2, ...] %}
20
...template content...
21
{% endcache %}
22
"""
23
```
24
25
**Automatic Registration:**
26
```python
27
from flask import Flask
28
from flask_caching import Cache
29
30
app = Flask(__name__)
31
cache = Cache(app) # Jinja2 extension automatically registered
32
33
# Disable automatic registration
34
cache = Cache(app, with_jinja2_ext=False)
35
```
36
37
### Basic Template Caching
38
39
Cache template fragments with timeout and optional cache keys.
40
41
```jinja2
42
{# Cache for 5 minutes with default key #}
43
{% cache 300 %}
44
<div class="expensive-content">
45
{% for item in expensive_database_query() %}
46
<p>{{ item.title }}: {{ item.description }}</p>
47
{% endfor %}
48
</div>
49
{% endcache %}
50
51
{# Cache with custom key #}
52
{% cache 600 "sidebar_content" %}
53
<aside class="sidebar">
54
{{ render_complex_sidebar() }}
55
</aside>
56
{% endcache %}
57
58
{# Cache with multiple keys for uniqueness #}
59
{% cache 300 "user_dashboard" user.id %}
60
<div class="dashboard">
61
<h2>Welcome {{ user.name }}</h2>
62
{{ render_user_stats(user.id) }}
63
</div>
64
{% endcache %}
65
```
66
67
### Dynamic Cache Keys
68
69
Use template variables and expressions to create dynamic cache keys based on context.
70
71
```jinja2
72
{# Cache per user and page #}
73
{% cache 600 "user_content" user.id request.endpoint %}
74
<div class="personalized-content">
75
{{ get_user_recommendations(user.id) }}
76
</div>
77
{% endcache %}
78
79
{# Cache with date-based key for daily content #}
80
{% cache 3600 "daily_stats" current_date.strftime('%Y-%m-%d') %}
81
<div class="daily-statistics">
82
{{ calculate_daily_metrics() }}
83
</div>
84
{% endcache %}
85
86
{# Cache with conditional keys #}
87
{% cache 300 "product_list" category.id, ("sale" if on_sale else "regular") %}
88
<div class="product-grid">
89
{% for product in get_products(category.id, on_sale) %}
90
{{ render_product_card(product) }}
91
{% endfor %}
92
</div>
93
{% endcache %}
94
```
95
96
### Cache Control Operations
97
98
Special cache operations within templates.
99
100
```jinja2
101
{# Delete cached content #}
102
{% cache 'del' "sidebar_content" %}
103
{# This will delete the cached content and re-render #}
104
<aside class="sidebar">
105
{{ render_complex_sidebar() }}
106
</aside>
107
{% endcache %}
108
109
{# Cache with no timeout (never expires) #}
110
{% cache None "permanent_footer" %}
111
<footer>
112
{{ render_footer_content() }}
113
</footer>
114
{% endcache %}
115
```
116
117
### Programmatic Cache Key Generation
118
119
Generate cache keys programmatically for template fragments.
120
121
```python { .api }
122
def make_template_fragment_key(
123
fragment_name: str,
124
vary_on: Optional[List[str]] = None
125
) -> str:
126
"""
127
Generate cache key for template fragments.
128
129
Parameters:
130
- fragment_name: Base name for the cache fragment
131
- vary_on: List of values to vary the cache key on
132
133
Returns:
134
String cache key for the template fragment
135
"""
136
```
137
138
**Usage in Python Code:**
139
```python
140
from flask_caching import make_template_fragment_key
141
142
# Generate keys manually
143
cache_key = make_template_fragment_key('user_profile', [str(user.id)])
144
cached_content = cache.get(cache_key)
145
146
if cached_content is None:
147
# Render template fragment
148
cached_content = render_template_string("""
149
<div class="profile">{{ user.name }}</div>
150
""", user=user)
151
cache.set(cache_key, cached_content, timeout=300)
152
153
# Delete specific fragment cache
154
cache_key = make_template_fragment_key('sidebar_content')
155
cache.delete(cache_key)
156
157
# Pre-warm cache
158
def pre_warm_user_cache(user_id):
159
cache_key = make_template_fragment_key('user_dashboard', [str(user_id)])
160
if not cache.has(cache_key):
161
user = User.query.get(user_id)
162
content = render_template('fragments/user_dashboard.html', user=user)
163
cache.set(cache_key, content, timeout=600)
164
```
165
166
### Template Fragment Cache Management
167
168
Manage cached template fragments from Python code.
169
170
```python
171
from flask import current_app
172
from flask_caching import make_template_fragment_key
173
174
def clear_user_template_cache(user_id):
175
"""Clear all cached template fragments for a user."""
176
fragment_keys = [
177
make_template_fragment_key('user_dashboard', [str(user_id)]),
178
make_template_fragment_key('user_profile', [str(user_id)]),
179
make_template_fragment_key('user_sidebar', [str(user_id)])
180
]
181
cache.delete_many(*fragment_keys)
182
183
def invalidate_category_cache(category_id):
184
"""Invalidate product listing caches for a category."""
185
keys_to_delete = [
186
make_template_fragment_key('product_list', [str(category_id), 'regular']),
187
make_template_fragment_key('product_list', [str(category_id), 'sale']),
188
make_template_fragment_key('category_sidebar', [str(category_id)])
189
]
190
cache.delete_many(*keys_to_delete)
191
192
def refresh_global_fragments():
193
"""Refresh site-wide cached fragments."""
194
global_fragments = ['header_nav', 'footer_content', 'sidebar_ads']
195
for fragment in global_fragments:
196
cache_key = make_template_fragment_key(fragment)
197
cache.delete(cache_key)
198
```
199
200
### Advanced Template Caching Patterns
201
202
Complex caching scenarios and best practices.
203
204
**Nested Fragment Caching:**
205
```jinja2
206
{# Outer cache for entire section #}
207
{% cache 1800 "news_section" %}
208
<section class="news">
209
<h2>Latest News</h2>
210
211
{# Inner cache for expensive news list #}
212
{% cache 300 "news_list" %}
213
<div class="news-list">
214
{% for article in get_latest_news() %}
215
<article>{{ article.title }}</article>
216
{% endfor %}
217
</div>
218
{% endcache %}
219
220
{# Separate cache for sidebar #}
221
{% cache 600 "news_sidebar" %}
222
<aside>{{ render_news_sidebar() }}</aside>
223
{% endcache %}
224
</section>
225
{% endcache %}
226
```
227
228
**Conditional Caching:**
229
```jinja2
230
{# Cache only for non-admin users #}
231
{% if not current_user.is_admin %}
232
{% cache 300 "public_content" %}
233
{% endif %}
234
235
<div class="content">
236
{{ expensive_content_generation() }}
237
</div>
238
239
{% if not current_user.is_admin %}
240
{% endcache %}
241
{% endif %}
242
```
243
244
**User-Specific Fragment Caching:**
245
```jinja2
246
{# Cache personalized content per user #}
247
{% cache 600 "personalized_recommendations" current_user.id %}
248
<div class="recommendations">
249
<h3>Recommended for {{ current_user.name }}</h3>
250
{% for item in get_user_recommendations(current_user.id) %}
251
<div class="recommendation">{{ item.title }}</div>
252
{% endfor %}
253
</div>
254
{% endcache %}
255
256
{# Cache with user role differentiation #}
257
{% cache 900 "dashboard_widgets" current_user.id current_user.role %}
258
<div class="dashboard-widgets">
259
{% if current_user.role == 'admin' %}
260
{{ render_admin_widgets() }}
261
{% elif current_user.role == 'manager' %}
262
{{ render_manager_widgets() }}
263
{% else %}
264
{{ render_user_widgets() }}
265
{% endif %}
266
</div>
267
{% endcache %}
268
```
269
270
## Template Caching Integration
271
272
The template cache extension integrates seamlessly with Flask-Caching's main cache instance.
273
274
**Cache Extension Constants:**
275
```python { .api }
276
JINJA_CACHE_ATTR_NAME = "_template_fragment_cache"
277
# Attribute name used to attach cache instance to Jinja2 environment
278
```
279
280
**Manual Extension Usage:**
281
```python
282
from flask_caching.jinja2ext import CacheExtension, JINJA_CACHE_ATTR_NAME
283
284
# Manual registration (when with_jinja2_ext=False)
285
app.jinja_env.add_extension(CacheExtension)
286
setattr(app.jinja_env, JINJA_CACHE_ATTR_NAME, cache)
287
288
# Access cache from template context
289
def get_template_cache():
290
return getattr(current_app.jinja_env, JINJA_CACHE_ATTR_NAME)
291
```
292
293
## Performance Considerations
294
295
**Cache Key Strategy:**
296
- Use specific, meaningful fragment names
297
- Include relevant variation parameters (user_id, date, category, etc.)
298
- Avoid overly long cache keys
299
- Consider cache key namespace conflicts
300
301
**Cache Timeout Strategy:**
302
- Short timeouts (1-5 minutes) for frequently changing content
303
- Medium timeouts (30-60 minutes) for semi-static content
304
- Long timeouts (several hours) for rarely changing content
305
- Use cache deletion for immediate updates when content changes
306
307
**Memory Usage:**
308
- Monitor cached fragment sizes
309
- Implement cache eviction strategies for large fragments
310
- Consider fragmenting very large template sections
311
- Use appropriate cache backends for template content size