0
# Template System
1
2
The template system provides template rendering framework supporting multiple template engines including Jinja2 and Mustache. It enables consistent template-based output generation for applications.
3
4
## Capabilities
5
6
### Template Handler Interface
7
8
Base interface for template rendering functionality that defines the contract for template operations.
9
10
```python { .api }
11
class TemplateHandler:
12
"""
13
Template handler interface for rendering templates with data.
14
15
Provides methods for loading template files and rendering them
16
with data contexts to produce formatted output.
17
"""
18
19
def load(self, template_path: str) -> Any:
20
"""
21
Load a template from file path.
22
23
Args:
24
template_path: Path to template file to load
25
26
Returns:
27
Loaded template object
28
"""
29
30
def render(self, content: str, data: Dict[str, Any]) -> str:
31
"""
32
Render template content with data context.
33
34
Args:
35
content: Template content string or template object
36
data: Dictionary of data to render with template
37
38
Returns:
39
Rendered template output string
40
"""
41
```
42
43
## Usage Examples
44
45
### Basic Template Usage
46
47
```python
48
from cement import App, Controller, ex, init_defaults
49
50
CONFIG = init_defaults('myapp')
51
CONFIG['template.jinja2'] = {
52
'template_dirs': ['./templates']
53
}
54
55
class TemplateController(Controller):
56
class Meta:
57
label = 'template'
58
stacked_on = 'base'
59
stacked_type = 'nested'
60
61
@ex(help='generate report')
62
def report(self):
63
"""Generate report using template."""
64
report_data = {
65
'title': 'Monthly Report',
66
'date': '2023-01-15',
67
'items': [
68
{'name': 'Sales', 'value': 15000, 'change': '+5%'},
69
{'name': 'Expenses', 'value': 8000, 'change': '+2%'},
70
{'name': 'Profit', 'value': 7000, 'change': '+8%'}
71
],
72
'summary': 'Overall performance improved this month'
73
}
74
75
# Load and render template
76
template = self.app.template.load('report.html')
77
output = self.app.template.render(template, report_data)
78
print(output)
79
80
class BaseController(Controller):
81
class Meta:
82
label = 'base'
83
84
class MyApp(App):
85
class Meta:
86
label = 'myapp'
87
base_controller = 'base'
88
extensions = ['jinja2']
89
template_handler = 'jinja2'
90
config_defaults = CONFIG
91
handlers = [BaseController, TemplateController]
92
93
with MyApp() as app:
94
app.run()
95
96
# Usage:
97
# myapp template report
98
```
99
100
### Jinja2 Template Engine
101
102
```python
103
from cement import App, Controller, ex, init_defaults
104
105
CONFIG = init_defaults('myapp')
106
CONFIG['template.jinja2'] = {
107
'template_dirs': ['./templates', './shared_templates'],
108
'auto_reload': True,
109
'cache_size': 50
110
}
111
112
class EmailController(Controller):
113
class Meta:
114
label = 'email'
115
stacked_on = 'base'
116
stacked_type = 'nested'
117
118
@ex(
119
help='send welcome email',
120
arguments=[
121
(['--user'], {'help': 'username', 'required': True}),
122
(['--email'], {'help': 'email address', 'required': True})
123
]
124
)
125
def welcome(self):
126
"""Send welcome email using Jinja2 template."""
127
user_data = {
128
'username': self.app.pargs.user,
129
'email': self.app.pargs.email,
130
'company': 'MyCompany',
131
'support_email': 'support@mycompany.com',
132
'features': [
133
'Dashboard access',
134
'Real-time notifications',
135
'Advanced reporting',
136
'24/7 support'
137
]
138
}
139
140
# Render email template
141
template = self.app.template.load('welcome_email.html')
142
email_html = self.app.template.render(template, user_data)
143
144
print(f"Welcome email generated for {user_data['username']}")
145
print("=" * 50)
146
print(email_html)
147
148
class BaseController(Controller):
149
class Meta:
150
label = 'base'
151
152
class MyApp(App):
153
class Meta:
154
label = 'myapp'
155
base_controller = 'base'
156
extensions = ['jinja2']
157
template_handler = 'jinja2'
158
config_defaults = CONFIG
159
handlers = [BaseController, EmailController]
160
161
with MyApp() as app:
162
app.run()
163
164
# Example template: templates/welcome_email.html
165
"""
166
<!DOCTYPE html>
167
<html>
168
<head>
169
<title>Welcome to {{ company }}</title>
170
</head>
171
<body>
172
<h1>Welcome, {{ username }}!</h1>
173
174
<p>Thank you for joining {{ company }}. Your account ({{ email }}) is now active.</p>
175
176
<h2>Available Features:</h2>
177
<ul>
178
{% for feature in features %}
179
<li>{{ feature }}</li>
180
{% endfor %}
181
</ul>
182
183
<p>Need help? Contact us at {{ support_email }}</p>
184
185
<p>Best regards,<br>
186
The {{ company }} Team</p>
187
</body>
188
</html>
189
"""
190
191
# Usage:
192
# myapp email welcome --user johndoe --email john@example.com
193
```
194
195
### Mustache Template Engine
196
197
```python
198
from cement import App, Controller, ex, init_defaults
199
200
CONFIG = init_defaults('myapp')
201
CONFIG['template.mustache'] = {
202
'template_dirs': ['./mustache_templates']
203
}
204
205
class ConfigController(Controller):
206
class Meta:
207
label = 'config'
208
stacked_on = 'base'
209
stacked_type = 'nested'
210
211
@ex(help='generate configuration file')
212
def generate(self):
213
"""Generate configuration file using Mustache template."""
214
config_data = {
215
'app_name': 'MyApplication',
216
'version': '1.0.0',
217
'database': {
218
'host': 'localhost',
219
'port': 5432,
220
'name': 'myapp_db'
221
},
222
'features': [
223
{'name': 'authentication', 'enabled': True},
224
{'name': 'caching', 'enabled': True},
225
{'name': 'logging', 'enabled': True},
226
{'name': 'monitoring', 'enabled': False}
227
],
228
'debug_mode': False
229
}
230
231
# Render configuration template
232
template = self.app.template.load('app_config.mustache')
233
config_output = self.app.template.render(template, config_data)
234
235
print("Generated configuration:")
236
print("=" * 40)
237
print(config_output)
238
239
class BaseController(Controller):
240
class Meta:
241
label = 'base'
242
243
class MyApp(App):
244
class Meta:
245
label = 'myapp'
246
base_controller = 'base'
247
extensions = ['mustache']
248
template_handler = 'mustache'
249
config_defaults = CONFIG
250
handlers = [BaseController, ConfigController]
251
252
with MyApp() as app:
253
app.run()
254
255
# Example template: mustache_templates/app_config.mustache
256
"""
257
# {{ app_name }} Configuration
258
# Version: {{ version }}
259
260
[application]
261
name = {{ app_name }}
262
version = {{ version }}
263
debug = {{ debug_mode }}
264
265
[database]
266
host = {{ database.host }}
267
port = {{ database.port }}
268
name = {{ database.name }}
269
270
[features]
271
{{#features}}
272
{{name}} = {{enabled}}
273
{{/features}}
274
"""
275
276
# Usage:
277
# myapp config generate
278
```
279
280
### Template with Custom Filters
281
282
```python
283
from cement import App, Controller, ex, init_defaults
284
import datetime
285
286
CONFIG = init_defaults('myapp')
287
CONFIG['template.jinja2'] = {
288
'template_dirs': ['./templates']
289
}
290
291
def datetime_filter(timestamp, format='%Y-%m-%d %H:%M:%S'):
292
"""Custom Jinja2 filter for datetime formatting."""
293
if isinstance(timestamp, str):
294
dt = datetime.datetime.fromisoformat(timestamp.replace('Z', '+00:00'))
295
else:
296
dt = timestamp
297
return dt.strftime(format)
298
299
def currency_filter(amount, symbol='$'):
300
"""Custom Jinja2 filter for currency formatting."""
301
return f"{symbol}{amount:,.2f}"
302
303
class ReportController(Controller):
304
class Meta:
305
label = 'report'
306
stacked_on = 'base'
307
stacked_type = 'nested'
308
309
@ex(help='generate sales report')
310
def sales(self):
311
"""Generate sales report with custom filters."""
312
sales_data = {
313
'report_title': 'Q1 Sales Report',
314
'generated_at': '2023-01-15T10:30:00Z',
315
'total_sales': 125000.50,
316
'transactions': [
317
{
318
'date': '2023-01-10T14:30:00Z',
319
'customer': 'Acme Corp',
320
'amount': 15000.00,
321
'status': 'completed'
322
},
323
{
324
'date': '2023-01-12T09:15:00Z',
325
'customer': 'Beta LLC',
326
'amount': 8500.75,
327
'status': 'completed'
328
}
329
]
330
}
331
332
# Add custom filters to Jinja2 environment
333
self.app.template.env.filters['datetime'] = datetime_filter
334
self.app.template.env.filters['currency'] = currency_filter
335
336
# Render report
337
template = self.app.template.load('sales_report.html')
338
report_html = self.app.template.render(template, sales_data)
339
340
print(report_html)
341
342
class BaseController(Controller):
343
class Meta:
344
label = 'base'
345
346
class MyApp(App):
347
class Meta:
348
label = 'myapp'
349
base_controller = 'base'
350
extensions = ['jinja2']
351
template_handler = 'jinja2'
352
config_defaults = CONFIG
353
handlers = [BaseController, ReportController]
354
355
with MyApp() as app:
356
app.run()
357
358
# Example template using custom filters: templates/sales_report.html
359
"""
360
<h1>{{ report_title }}</h1>
361
<p>Generated: {{ generated_at | datetime('%B %d, %Y at %I:%M %p') }}</p>
362
363
<h2>Summary</h2>
364
<p>Total Sales: {{ total_sales | currency }}</p>
365
366
<h2>Transactions</h2>
367
<table>
368
<tr>
369
<th>Date</th>
370
<th>Customer</th>
371
<th>Amount</th>
372
<th>Status</th>
373
</tr>
374
{% for transaction in transactions %}
375
<tr>
376
<td>{{ transaction.date | datetime('%m/%d/%Y') }}</td>
377
<td>{{ transaction.customer }}</td>
378
<td>{{ transaction.amount | currency }}</td>
379
<td>{{ transaction.status | title }}</td>
380
</tr>
381
{% endfor %}
382
</table>
383
"""
384
385
# Usage:
386
# myapp report sales
387
```
388
389
### Template Inheritance
390
391
```python
392
from cement import App, Controller, ex, init_defaults
393
394
CONFIG = init_defaults('myapp')
395
CONFIG['template.jinja2'] = {
396
'template_dirs': ['./templates']
397
}
398
399
class PageController(Controller):
400
class Meta:
401
label = 'page'
402
stacked_on = 'base'
403
stacked_type = 'nested'
404
405
@ex(help='generate dashboard page')
406
def dashboard(self):
407
"""Generate dashboard page using template inheritance."""
408
dashboard_data = {
409
'page_title': 'Dashboard',
410
'user': {
411
'name': 'John Doe',
412
'role': 'Administrator'
413
},
414
'stats': [
415
{'label': 'Total Users', 'value': 1250, 'trend': 'up'},
416
{'label': 'Active Sessions', 'value': 89, 'trend': 'up'},
417
{'label': 'System Load', 'value': '65%', 'trend': 'stable'}
418
],
419
'recent_activity': [
420
'User johndoe logged in',
421
'New user registered: alice',
422
'System backup completed'
423
]
424
}
425
426
template = self.app.template.load('dashboard.html')
427
page_html = self.app.template.render(template, dashboard_data)
428
429
print(page_html)
430
431
class BaseController(Controller):
432
class Meta:
433
label = 'base'
434
435
class MyApp(App):
436
class Meta:
437
label = 'myapp'
438
base_controller = 'base'
439
extensions = ['jinja2']
440
template_handler = 'jinja2'
441
config_defaults = CONFIG
442
handlers = [BaseController, PageController]
443
444
with MyApp() as app:
445
app.run()
446
447
# Base template: templates/base.html
448
"""
449
<!DOCTYPE html>
450
<html>
451
<head>
452
<title>{% block title %}MyApp{% endblock %}</title>
453
<style>
454
body { font-family: Arial, sans-serif; margin: 20px; }
455
.header { background: #f0f0f0; padding: 10px; }
456
.content { margin: 20px 0; }
457
.footer { border-top: 1px solid #ccc; padding: 10px; }
458
</style>
459
</head>
460
<body>
461
<div class="header">
462
<h1>{% block header %}MyApp{% endblock %}</h1>
463
{% block nav %}
464
<nav>
465
<a href="/">Home</a> |
466
<a href="/dashboard">Dashboard</a> |
467
<a href="/settings">Settings</a>
468
</nav>
469
{% endblock %}
470
</div>
471
472
<div class="content">
473
{% block content %}{% endblock %}
474
</div>
475
476
<div class="footer">
477
{% block footer %}
478
<p>© 2023 MyApp. All rights reserved.</p>
479
{% endblock %}
480
</div>
481
</body>
482
</html>
483
"""
484
485
# Dashboard template: templates/dashboard.html
486
"""
487
{% extends "base.html" %}
488
489
{% block title %}{{ page_title }} - MyApp{% endblock %}
490
491
{% block header %}{{ page_title }}{% endblock %}
492
493
{% block content %}
494
<p>Welcome back, {{ user.name }} ({{ user.role }})</p>
495
496
<h2>System Statistics</h2>
497
<div class="stats">
498
{% for stat in stats %}
499
<div class="stat-item">
500
<strong>{{ stat.label }}:</strong> {{ stat.value }}
501
<span class="trend-{{ stat.trend }}">{{ stat.trend }}</span>
502
</div>
503
{% endfor %}
504
</div>
505
506
<h2>Recent Activity</h2>
507
<ul>
508
{% for activity in recent_activity %}
509
<li>{{ activity }}</li>
510
{% endfor %}
511
</ul>
512
{% endblock %}
513
"""
514
515
# Usage:
516
# myapp page dashboard
517
```