or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

context.mdcore-application.mdhelpers.mdindex.mdrequest-response.mdsignals.mdtemplates.mdtesting.mdwebsocket.md

templates.mddocs/

0

# Template System

1

2

Async Jinja2 template rendering with streaming support, Flask-compatible template context, and comprehensive template environment customization.

3

4

## Capabilities

5

6

### Template Rendering Functions

7

8

Async template rendering with full Jinja2 support and streaming capabilities for large templates.

9

10

```python { .api }

11

async def render_template(template_name_or_list: str | list[str], **context) -> str:

12

"""

13

Render template with given context.

14

15

Args:

16

template_name_or_list: Template name or list of template names (first found is used)

17

**context: Template context variables

18

19

Returns:

20

Rendered template as string

21

"""

22

23

async def render_template_string(source: str, **context) -> str:

24

"""

25

Render template from string source.

26

27

Args:

28

source: Template source code

29

**context: Template context variables

30

31

Returns:

32

Rendered template as string

33

"""

34

35

async def stream_template(template_name_or_list: str | list[str], **context):

36

"""

37

Stream template rendering for large templates.

38

39

Args:

40

template_name_or_list: Template name or list of template names

41

**context: Template context variables

42

43

Returns:

44

AsyncIterator yielding template chunks

45

"""

46

47

async def stream_template_string(source: str, **context):

48

"""

49

Stream template rendering from string source.

50

51

Args:

52

source: Template source code

53

**context: Template context variables

54

55

Returns:

56

AsyncIterator yielding template chunks

57

"""

58

```

59

60

### Template Environment

61

62

Quart-specific Jinja2 environment with async support and Flask-compatible context.

63

64

```python { .api }

65

class Environment:

66

"""

67

Quart-specific Jinja2 environment with async template support.

68

69

Provides Flask-compatible template context with additional async capabilities

70

and integration with Quart's request/response cycle.

71

"""

72

73

def get_template(self, name: str | list[str]):

74

"""Get template by name."""

75

76

def from_string(self, source: str):

77

"""Create template from string source."""

78

79

def select_template(self, names: list[str]):

80

"""Select first available template from list."""

81

```

82

83

### Usage Examples

84

85

#### Basic Template Rendering

86

87

```python

88

from quart import Quart, render_template, request

89

90

app = Quart(__name__)

91

92

@app.route('/')

93

async def index():

94

# Simple template rendering

95

return await render_template('index.html',

96

title='Welcome',

97

message='Hello, Quart!')

98

99

@app.route('/user/<username>')

100

async def user_profile(username):

101

# Template with dynamic data

102

user_data = await get_user_data(username)

103

return await render_template('user.html',

104

user=user_data,

105

current_user=await get_current_user())

106

107

@app.route('/dashboard')

108

async def dashboard():

109

# Template with multiple data sources

110

stats = await get_dashboard_stats()

111

recent_activity = await get_recent_activity()

112

notifications = await get_notifications()

113

114

return await render_template('dashboard.html',

115

stats=stats,

116

activity=recent_activity,

117

notifications=notifications,

118

page_title='Dashboard')

119

```

120

121

#### Template with Request Context

122

123

```python

124

from quart import Quart, render_template, request, g

125

126

app = Quart(__name__)

127

128

@app.before_request

129

async def before_request():

130

# Set up global template context

131

g.current_time = datetime.now()

132

g.user_agent = request.headers.get('User-Agent', '')

133

134

@app.context_processor

135

async def inject_template_vars():

136

# Add variables to all templates

137

return {

138

'app_name': 'My Quart App',

139

'version': '1.0.0',

140

'current_time': g.current_time,

141

'is_mobile': 'Mobile' in g.user_agent

142

}

143

144

@app.route('/page')

145

async def page():

146

# Template automatically has access to injected variables

147

return await render_template('page.html',

148

content='Page content here')

149

```

150

151

#### Template String Rendering

152

153

```python

154

from quart import Quart, render_template_string

155

156

app = Quart(__name__)

157

158

@app.route('/dynamic')

159

async def dynamic_template():

160

# Render template from string (useful for dynamic templates)

161

template_source = """

162

<h1>{{ title }}</h1>

163

<p>Current time: {{ current_time }}</p>

164

{% for item in items %}

165

<li>{{ item.name }}: {{ item.value }}</li>

166

{% endfor %}

167

"""

168

169

return await render_template_string(

170

template_source,

171

title='Dynamic Template',

172

current_time=datetime.now(),

173

items=[

174

{'name': 'Item 1', 'value': 'Value 1'},

175

{'name': 'Item 2', 'value': 'Value 2'}

176

]

177

)

178

179

@app.route('/email/preview')

180

async def email_preview():

181

# Generate email template preview

182

email_template = await get_email_template('welcome')

183

user_data = await get_sample_user_data()

184

185

return await render_template_string(

186

email_template.content,

187

user=user_data,

188

company_name='My Company',

189

unsubscribe_url='https://example.com/unsubscribe'

190

)

191

```

192

193

#### Streaming Templates

194

195

```python

196

from quart import Quart, Response, stream_template

197

import asyncio

198

199

app = Quart(__name__)

200

201

@app.route('/large-report')

202

async def large_report():

203

# Stream large template for better performance

204

async def generate():

205

async for chunk in stream_template('large_report.html',

206

data=await get_large_dataset(),

207

title='Annual Report'):

208

yield chunk

209

210

return Response(generate(), mimetype='text/html')

211

212

@app.route('/live-data')

213

async def live_data():

214

# Stream template with real-time data updates

215

async def generate():

216

template_start = """

217

<html><head><title>Live Data</title></head><body>

218

<h1>Live Data Stream</h1>

219

<div id="data">

220

"""

221

yield template_start

222

223

# Stream data updates

224

for i in range(100):

225

data_chunk = f"<p>Data point {i}: {await get_live_data_point()}</p>\n"

226

yield data_chunk

227

await asyncio.sleep(0.1) # Simulate real-time updates

228

229

template_end = """

230

</div>

231

<script>

232

// Auto-scroll to bottom

233

window.scrollTo(0, document.body.scrollHeight);

234

</script>

235

</body></html>

236

"""

237

yield template_end

238

239

return Response(generate(), mimetype='text/html')

240

241

async def get_large_dataset():

242

# Simulate large dataset

243

return [{'id': i, 'value': f'Item {i}'} for i in range(10000)]

244

245

async def get_live_data_point():

246

# Simulate live data

247

return f"Value at {datetime.now()}"

248

```

249

250

#### Custom Template Functions and Filters

251

252

```python

253

from quart import Quart, render_template

254

from markupsafe import Markup

255

import json

256

257

app = Quart(__name__)

258

259

@app.template_filter('jsonify')

260

def jsonify_filter(value):

261

"""Custom filter to convert Python objects to JSON."""

262

return Markup(json.dumps(value, indent=2))

263

264

@app.template_filter('currency')

265

def currency_filter(value):

266

"""Format value as currency."""

267

try:

268

return f"${float(value):.2f}"

269

except (ValueError, TypeError):

270

return "$0.00"

271

272

@app.template_global()

273

def get_menu_items():

274

"""Global function available in all templates."""

275

return [

276

{'name': 'Home', 'url': '/'},

277

{'name': 'About', 'url': '/about'},

278

{'name': 'Contact', 'url': '/contact'}

279

]

280

281

@app.template_test('even')

282

def is_even(value):

283

"""Custom test for even numbers."""

284

try:

285

return int(value) % 2 == 0

286

except (ValueError, TypeError):

287

return False

288

289

@app.route('/demo')

290

async def template_demo():

291

return await render_template('demo.html',

292

data={'items': [1, 2, 3, 4, 5]},

293

price=29.99)

294

```

295

296

Example `demo.html` template using custom functions:

297

298

```html

299

<!DOCTYPE html>

300

<html>

301

<head>

302

<title>Template Demo</title>

303

</head>

304

<body>

305

<nav>

306

<ul>

307

{% for item in get_menu_items() %}

308

<li><a href="{{ item.url }}">{{ item.name }}</a></li>

309

{% endfor %}

310

</ul>

311

</nav>

312

313

<h1>Demo Page</h1>

314

315

<p>Price: {{ price | currency }}</p>

316

317

<h2>Data (JSON format):</h2>

318

<pre>{{ data | jsonify }}</pre>

319

320

<h2>Numbers:</h2>

321

{% for item in data.items %}

322

<p>{{ item }} is {% if item is even %}even{% else %}odd{% endif %}</p>

323

{% endfor %}

324

</body>

325

</html>

326

```

327

328

#### Error Handling in Templates

329

330

```python

331

from quart import Quart, render_template, abort

332

from jinja2 import TemplateNotFound, TemplateSyntaxError

333

334

app = Quart(__name__)

335

336

@app.route('/safe-render/<template_name>')

337

async def safe_render(template_name):

338

try:

339

# Safely render user-specified template

340

safe_templates = ['page1.html', 'page2.html', 'page3.html']

341

342

if template_name not in safe_templates:

343

abort(404)

344

345

return await render_template(f'safe/{template_name}',

346

content='Safe content')

347

348

except TemplateNotFound:

349

# Handle missing template

350

return await render_template('error.html',

351

error='Template not found',

352

code=404), 404

353

354

@app.errorhandler(TemplateSyntaxError)

355

async def handle_template_error(error):

356

# Handle template syntax errors

357

if app.debug:

358

return f"Template Error: {error}", 500

359

else:

360

return await render_template('error.html',

361

error='Template rendering failed',

362

code=500), 500

363

364

@app.route('/fallback-template')

365

async def fallback_template():

366

# Try multiple templates, use first found

367

template_options = [

368

'custom_page.html',

369

'default_page.html',

370

'fallback.html'

371

]

372

373

return await render_template(template_options,

374

title='Fallback Demo',

375

message='This uses template fallback')

376

```