or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

backends.mdcore-operations.mddecorators.mdindex.mdtemplate-caching.md

template-caching.mddocs/

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