comparison doc/xml-templates.txt @ 226:09f869a98149

Add reStructuredText documentation files.
author cmlenz
date Fri, 08 Sep 2006 08:44:31 +0000
parents
children 24757b771651
comparison
equal deleted inserted replaced
225:0edf663b97d6 226:09f869a98149
1 .. -*- mode: rst; encoding: utf-8 -*-
2
3 ============================
4 Markup XML Template Language
5 ============================
6
7 Markup provides a simple XML-based template language that is heavily inspired
8 by Kid_, which in turn was inspired by a number of existing template languages,
9 namely XSLT_, TAL_, and PHP_.
10
11 .. _kid: http://kid-templating.org/
12 .. _python: http://www.python.org/
13 .. _xslt: http://www.w3.org/TR/xslt
14 .. _tal: http://www.zope.org/Wikis/DevSite/Projects/ZPT/TAL
15 .. _php: http://www.php.net/
16
17 This document describes the template language and will be most useful as
18 reference to those developing Markup templates. Templates are XML files of some
19 kind (such as XHTML) that include processing directives_ (elements or
20 attributes identified by a separate namespace) that affect how the template is
21 rendered, and template expressions_ that are dynamically substituted by
22 variable data.
23
24
25 .. contents:: Contents
26 :depth: 3
27 .. sectnum::
28
29 ----------
30 Python API
31 ----------
32
33 The Python code required for templating with Markup is generally based on the
34 following pattern:
35
36 * Attain a ``Template`` object from a string or file object containing the
37 template XML source. This can either be done directly, or through a
38 ``TemplateLoader`` instance.
39 * Call the ``generate()`` method of the template, passing any data that should
40 be made available to the template as keyword arguments.
41 * Serialize the resulting stream using its ``render()`` method.
42
43 For example::
44
45 from markup.template import Template
46
47 tmpl = Template('<h1>$title</h1>')
48 stream = tmpl.generate(title='Hello, world!')
49 print stream.render('xhtml')
50
51 That code would produce the following output::
52
53 <h1>Hello, world!</h1>
54
55 However, if you want includes_ to work, you should attain the template instance
56 through a ``TemplateLoader``, and load the template from a file::
57
58 from markup.template import TemplateLoader
59
60 loader = TemplateLoader([templates_dir])
61 tmpl = loader.load('test.html')
62 stream = tmpl.generate(title='Hello, world!')
63 print stream.render('xhtml')
64
65
66 .. _`expressions`:
67
68 --------------------
69 Template Expressions
70 --------------------
71
72 Python_ expressions can be used in text and attribute values. An expression is
73 substituted with the result of its evaluation against the template data.
74 Expressions need to prefixed with a dollar sign (``$``) and usually enclosed in
75 curly braces (``{…}``).
76
77 If the expression starts with a letter and contains only letters and digits,
78 the curly braces may be omitted. In all other cases, the braces are required so
79 that the template processors knows where the expression ends::
80
81 >>> from markup.template import Context, Template
82 >>> tmpl = Template('<em>${items[0].capitalize()} item</em>')
83 >>> print tmpl.generate(Context(items=['first', 'second']))
84 <em>First item</em>
85
86 Expressions support the full power of Python. In addition, it is possible to
87 access items in a dictionary using “dotted notation” (i.e. as if they were
88 attributes), and vice-versa (i.e. access attributes as if they were items in a
89 dictionary)::
90
91 >>> from markup.template import Context, Template
92 >>> tmpl = Template('<em>${dict.foo}</em>')
93 >>> print tmpl.generate(Context(dict={'foo': 'bar'}))
94 <em>bar</em>
95
96
97 .. _`directives`:
98
99 -------------------
100 Template Directives
101 -------------------
102
103 Directives are elements and/or attributes in the template that are identified
104 by the namespace ``http://markup.edgewall.org/``. They can affect how the
105 template is rendered in a number of ways: Markup provides directives for
106 conditionals and looping, among others.
107
108 To use directives in a template, the namespace should be declared, which is
109 usually done on the root element::
110
111 <html xmlns="http://www.w3.org/1999/xhtml"
112 xmlns:py="http://markup.edgewall.org/"
113 lang="en">
114 ...
115 </html>
116
117 In this example, the default namespace is set to the XHTML namespace, and the
118 namespace for Markup directives is bound to the prefix “py”.
119
120 All directives can be applied as attributes, and some can also be used as
121 elements. The ``if`` directives for conditionals, for example, can be used in
122 both ways::
123
124 <html xmlns="http://www.w3.org/1999/xhtml"
125 xmlns:py="http://markup.edgewall.org/"
126 lang="en">
127 ...
128 <div py:if="foo">
129 <p>Bar</p>
130 </div>
131 ...
132 </html>
133
134 This is basically equivalent to the following::
135
136 <html xmlns="http://www.w3.org/1999/xhtml"
137 xmlns:py="http://markup.edgewall.org/"
138 lang="en">
139 ...
140 <py:if test="foo">
141 <div>
142 <p>Bar</p>
143 </div>
144 </py:if>
145 ...
146 </html>
147
148 The rationale behind the second form is that directives do not always map
149 naturally to elements in the template. In such cases, the ``py:strip``
150 directive can be used to strip off the unwanted element, or the directive can
151 simply be used as an element.
152
153
154 Available Directives
155 ====================
156
157
158 .. _`py:attrs`:
159
160 ``py:attrs``
161 ------------
162
163 This directive adds, modifies or removes attributes from the element::
164
165 <ul>
166 <li py:attrs="foo">Bar</li>
167 </ul>
168
169 Given ``foo={'class': 'collapse'}`` in the template context, this would
170 produce::
171
172 <ul>
173 <li class="collapse">Bar</li>
174 </ul>
175
176 Attributes with the value ``None`` are omitted, so given ``foo={'class': None}``
177 in the context for the same template this would produce::
178
179 <ul>
180 <li>Bar</li>
181 </ul>
182
183 This directive can only be used as an attribute.
184
185
186 .. _`py:choose`:
187 .. _`py:when`:
188 .. _`py:otherwise`:
189
190 ``py:choose`` / ``py:when`` / ``py:otherwise``
191 ----------------------------------------------
192
193 This set of directives provides advanced contional processing for rendering one
194 of several alternatives. The first matching ``py:when`` branch is rendered, or,
195 if no ``py:when`` branch matches, the ``py:otherwise`` branch is be rendered.
196
197 If the ``py:choose`` directive is empty the nested ``py:when`` directives will
198 be tested for truth::
199
200 <div py:choose="">
201 <span py:when="0 == 1">0</span>
202 <span py:when="1 == 1">1</span>
203 <span py:otherwise="">2</span>
204 </div>
205
206 This would produce the following output::
207
208 <div>
209 <span>1</span>
210 </div>
211
212 If the ``py:choose`` directive contains an expression the nested ``py:when``
213 directives will be tested for equality to the parent ``py:choose`` value::
214
215 <div py:choose="1">
216 <span py:when="0">0</span>
217 <span py:when="1">1</span>
218 <span py:otherwise="">2</span>
219 </div>
220
221 This would produce the following output::
222
223 <div>
224 <span>1</span>
225 </div>
226
227
228 .. _`py:content`:
229
230 ``py:content``
231 --------------
232
233 This directive replaces any nested content with the result of evaluating the
234 expression::
235
236 <ul>
237 <li py:content="bar">Hello</li>
238 </ul>
239
240 Given ``bar='Bye'`` in the context data, this would produce::
241
242 <ul>
243 <li>Bye</li>
244 </ul>
245
246 This directive can only be used as an attribute.
247
248
249 .. _`py:def`:
250 .. _`macros`:
251
252 ``py:def``
253 ----------
254
255 The ``py:def`` directive can be used to create macros, i.e. snippets of
256 template code that have a name and optionally some parameters, and that can be
257 inserted in other places::
258
259 <div>
260 <p py:def="greeting(name)" class="greeting">
261 Hello, ${name}!
262 </p>
263 ${greeting('world')}
264 ${greeting('everyone else')}
265 </div>
266
267 The above would be rendered to::
268
269 <div>
270 <p class="greeting">
271 Hello, world!
272 </p>
273 <p class="greeting">
274 Hello, everyone else!
275 </p>
276 </div>
277
278 If a macro doesn't require parameters, it can be defined as well as called
279 without the parenthesis. For example::
280
281 <div>
282 <p py:def="greeting" class="greeting">
283 Hello, world!
284 </p>
285 ${greeting}
286 </div>
287
288 The above would be rendered to::
289
290 <div>
291 <p class="greeting">
292 Hello, world!
293 </p>
294 </div>
295
296 This directive can also be used as an element::
297
298 <div>
299 <py:def function="greeting(name)">
300 <p class="greeting">Hello, ${name}!</p>
301 </py:def>
302 </div>
303
304
305 .. _`py:for`:
306
307 ``py:for``
308 ----------
309
310 The element is repeated for every item in an iterable::
311
312 <ul>
313 <li py:for="item in items">${item}</li>
314 </ul>
315
316 Given ``items=[1, 2, 3]`` in the context data, this would produce::
317
318 <ul>
319 <li>1</li><li>2</li><li>3</li>
320 </ul>
321
322 This directive can also be used as an element::
323
324 <ul>
325 <py:for each="item in items">
326 <li>${item}</li>
327 </py:for>
328 </ul>
329
330
331 .. _`py:if`:
332
333 ``py:if``
334 ------------
335
336 The element is only rendered if the expression evaluates to a truth value::
337
338 <div>
339 <b py:if="foo">${bar}</b>
340 </div>
341
342 Given the data ``foo=True`` and ``bar='Hello'`` in the template context, this
343 would produce::
344
345 <div>
346 <b>Hello</b>
347 </div>
348
349 This directive can also be used as an element::
350
351 <div>
352 <py:if test="foo">
353 <b>${bar}</b>
354 </py:if>
355 </div>
356
357
358 .. _`py:match`:
359 .. _Match Templates:
360
361 ``py:match``
362 ------------
363
364 This directive defines a *match template*: given an XPath expression, it
365 replaces any element in the template that matches the expression with its own
366 content.
367
368 For example, the match template defined in the following template matches any
369 element with the tag name “greeting”::
370
371 <div>
372 <span py:match="greeting">
373 Hello ${select('@name')}
374 </span>
375 <greeting name="Dude" />
376 </div>
377
378 This would result in the following output::
379
380 <div>
381 <span>
382 Hello Dude
383 </span>
384 </div>
385
386 Inside the body of a ``py:match`` directive, the ``select(path)`` function is
387 made available so that parts or all of the original element can be incorporated
388 in the output of the match template. See [wiki:MarkupStream#UsingXPath] for
389 more information about this function.
390
391 This directive can also be used as an element::
392
393 <div>
394 <py:match path="greeting">
395 <span>Hello ${select('@name')}</span>
396 </py:match>
397 <greeting name="Dude" />
398 </div>
399
400
401 .. _`py:replace`:
402
403 ``py:replace``
404 --------------
405
406 This directive replaces the element itself with the result of evaluating the
407 expression::
408
409 <div>
410 <span py:replace="bar">Hello</span>
411 </div>
412
413 Given ``bar='Bye'`` in the context data, this would produce::
414
415 <div>
416 Bye
417 </div>
418
419 This directive can only be used as an attribute.
420
421
422 .. _`py:strip`:
423
424 ``py:strip``
425 ------------
426
427 This directive conditionally strips the top-level element from the output. When
428 the value of the ``py:strip`` attribute evaluates to ``True``, the element is
429 stripped from the output::
430
431 <div>
432 <div py:strip="True"><b>foo</b></div>
433 </div>
434
435 This would be rendered as::
436
437 <div>
438 <b>foo</b>
439 </div>
440
441 As a shorthand, if the value of the ``py:strip`` attribute is empty, that has
442 the same effect as using a truth value (i.e. the element is stripped).
443
444
445 .. _`with`:
446
447 ``py:with``
448 -----------
449
450 The ``py:with`` directive lets you assign expressions to variables, which can
451 be used to make expressions inside the directive less verbose and more
452 efficient. For example, if you need use the expression ``author.posts`` more
453 than once, and that actually results in a database query, assigning the results
454 to a variable using this directive would probably help.
455
456 For example::
457
458 <div>
459 <span py:with="y=7; z=x+10">$x $y $z</span>
460 </div>
461
462 Given ``x=42`` in the context data, this would produce::
463
464 <div>
465 <span>42 7 52</span>
466 </div>
467
468 This directive can also be used as an element::
469
470 <div>
471 <py:with vars="y=7; z=x+10">$x $y $z</py:with>
472 </div>
473
474 Note that if a variable of the same name already existed outside of the scope
475 of the ``py:with`` directive, it will **not** be overwritten. Instead, it
476 will have the same value it had prior to the ``py:with`` assignment.
477 Effectively, this means that variables are immutable in Markup.
478
479
480 .. _order:
481
482 Processing Order
483 ================
484
485 It is possible to attach multiple directives to a single element, although not
486 all combinations make sense. When multiple directives are encountered, they are
487 processed in the following order:
488
489 #. `py:def`_
490 #. `py:match`_
491 #. `py:when`_
492 #. `py:otherwise`_
493 #. `py:for`_
494 #. `py:if`_
495 #. `py:choose`_
496 #. `py:with`_
497 #. `py:replace`_
498 #. `py:content`_
499 #. `py:attrs`_
500 #. `py:strip`_
501
502
503 .. _includes:
504
505 --------
506 Includes
507 --------
508
509 To reuse common snippets of template code, you can include other files using
510 XInclude_.
511
512 .. _xinclude: http://www.w3.org/TR/xinclude/
513
514 For this, you need to declare the XInclude namespace (commonly bound to the
515 prefix “xi”) and use the ``<xi:include>`` element where you want the external
516 file to be pulled in::
517
518 <html xmlns="http://www.w3.org/1999/xhtml"
519 xmlns:py="http://markup.edgewall.org/"
520 xmlns:xi="http://www.w3.org/2001/XInclude">
521 <xi:include href="base.html" />
522 ...
523 </html>
524
525 Include paths are relative to the filename of the template currently being
526 processed. So if the example above was in the file "``myapp/index.html``"
527 (relative to the template search path), the XInclude processor would look for
528 the included file at "``myapp/base.html``". You can also use Unix-style
529 relative paths, for example "``../base.html``" to look in the parent directory.
530
531 Any content included this way is inserted into the generated output instead of
532 the ``<xi:include>`` element. The included template sees the same context data.
533 `Match templates`_ and `macros`_ in the included template are also available to
534 the including template after the point it was included.
535
536 By default, an error will be raised if an included file is not found. If that's
537 not what you want, you can specify fallback content that should be used if the
538 include fails. For example, to to make the include above fail silently, you'd
539 write:
540
541 <xi:include href="base.html"><xi:fallback /></xi:include>
542
543 See the XInclude_ for more about fallback content. Note though that Markup
544 currently only supports a small subset of XInclude.
545
546 Incudes in Markup are fully dynamic: Just like normal attributes, the `href`
547 attribute accepts expressions_, and directives_ can be used on the
548 ``<xi:include />`` element just as on any other element, meaning you can do
549 things like conditional includes::
550
551 <xi:include href="${name}.html" py:if="not in_popup"
552 py:for="name in ('foo', 'bar', 'baz')" />
553
554
555 .. _comments:
556
557 --------
558 Comments
559 --------
560
561 Normal XML/HTML comment syntax can be used in templates::
562
563 <!-- this is a comment -->
564
565 However, such comments get passed through the processing pipeline and are by
566 default included in the final output. If that's not desired, prefix the comment
567 text with an exclamation mark::
568
569 <!-- !this is a comment too, but one that will be stripped from the output -->
570
571 Note that it does not matter whether there's whitespace before or after the
572 exclamation mark, so the above could also be written as follows::
573
574 <!--! this is a comment too, but one that will be stripped from the output -->
Copyright (C) 2012-2017 Edgewall Software