Migrating from Genshi¶
Kajiki uses syntax derived from the syntax of Genshi. In particular, the following directives are supported, with semantics intended to be nearly identical to those of Genshi.
py:def
py:choose
– renamedpy:with
py:when
– renamedpy:case
py:otherwise
– renamedpy:else
py:for
py:if
py:with
py:replace
py:content
py:attrs
py:strip
xi:include
– renamedpy:include
Note that, in particular, py:match in Kajiki differs from Genshi, implementing PEP634.
Kajiki also supports the following additional directives not in Genshi:
py:extends
- indicates that this is an extension template. The parent template will be read in and used for layout, with anypy:block
directives in the child template overriding thepy:block
directives defined in the parent.
py:block
- used to name a replaceable ‘slot’ in a parent template, or to specify a slot override in a child template. Thepy:block
semantics are modeled after the{% block %}
semantics of Jinja2.
Generally, migration consists of a few steps that can be simple or quite
difficult based on your fondness of the py:match
directive in Genshi. In
simple cases where you have one master.html
template with a few py:match
directives that is xi:included
into all your page templates, the following
steps should suffice:
Rename tags and attributes as indicated above; e.g.
xi:include
becomespy:include
.Rewrite your include directives to use Kajiki’s module naming system and relative imports.
In a simple case where you have only a few
py:match
directives, all of which are in amaster.html
template that is being included from child templates, I recommend that you rewrite themaster.html
aslayout.html
, defining namedpy:block
regions that will be overridden in child templates.In your child templates, remove the
<xi:include href="master.html">
that probably lurks near the top. Then add apy:extends
directive to the top-level tag (usually<html>
). The tag the parts of the child template that are intended to override parts of the parent template with thepy:block
directive.
Kajiki also provides some helper functions of Genshi:
defined('some_variable')
(which returns True if ‘some_variable’ exists in the template context),value_of('name', default_value)
, andMarkup(some_string)
(which marks a string so it won’t be escaped in the output), though Kajiki prefers to call thisliteral(some_string)
.
Example Migration¶
Suppose you have a couple of Genshi templates, one of which called master.html
and one of which is index.html
. (TurboGears developers may recognize these
files as slightly modified versions of the default templates deposited in a TG
quickstarted project.) The contents of master.html
are:
1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3<html xmlns="http://www.w3.org/1999/xhtml"
4 xmlns:py="http://genshi.edgewall.org/"
5 xmlns:xi="http://www.w3.org/2001/XInclude"
6 py:strip="">
7 <xi:include href="header.html" />
8 <xi:include href="sidebars.html" />
9 <xi:include href="footer.html" />
10<head py:match="head" py:attrs="select('@*')">
11 <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
12 <title py:replace="''">Your title goes here</title>
13 <meta py:replace="select('*')"/>
14 <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/style.css')}" />
15 <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/admin.css')}" />
16</head>
17
18<body py:match="body" py:attrs="select('@*')">
19 ${header()}
20 <ul id="mainmenu">
21 <li class="first"><a href="${tg.url('/')}" class="${('', 'active')[defined('page') and page=='index']}">Welcome</a></li>
22 <li><a href="${tg.url('/about')}" class="${('', 'active')[defined('page') and page=='about']}">About</a></li>
23 <li py:if="tg.auth_stack_enabled"><a href="${tg.url('/auth')}" class="${('', 'active')[defined('page') and page=='auth']}">Authentication</a></li>
24 <li><a href="${tg.url('/environ')}" class="${('', 'active')[defined('page') and page=='environ']}">WSGI Environment</a></li>
25 <li><a href="http://groups.google.com/group/turbogears">Contact</a></li>
26 <span py:if="tg.auth_stack_enabled" py:strip="True">
27 <li py:if="not request.identity" id="login" class="loginlogout"><a href="${tg.url('/login')}">Login</a></li>
28 <li py:if="request.identity" id="login" class="loginlogout"><a href="${tg.url('/logout_handler')}">Logout</a></li>
29 <li py:if="request.identity" id="admin" class="loginlogout"><a href="${tg.url('/admin')}">Admin</a></li>
30 </span>
31 </ul>
32 <div id="content">
33 <py:if test="defined('page')">
34 <div class="currentpage">
35 Now Viewing: <span py:replace="page"/>
36 </div>
37 </py:if>
38 <py:with vars="flash=tg.flash_obj.render('flash', use_js=False)">
39 <div py:if="flash" py:content="XML(flash)" />
40 </py:with>
41 <div py:replace="select('*|text()')"/>
42 <!-- End of main_content -->
43 ${footer()}
44 </div>
45</body>
46</html>
Likewise, the contents of index.html
are as follows:
1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3<html xmlns="http://www.w3.org/1999/xhtml"
4 xmlns:py="http://genshi.edgewall.org/"
5 xmlns:xi="http://www.w3.org/2001/XInclude">
6
7 <xi:include href="master.html" />
8
9<head>
10 <meta content="text/html; charset=UTF-8" http-equiv="content-type" py:replace="''"/>
11 <title>Welcome to TurboGears 2.0, standing on the
12 shoulders of giants, since 2007</title>
13</head>
14
15<body>
16 ${sidebar_top()}
17 <div id="getting_started">
18 <h2>Presentation</h2>
19 <p>TurboGears 2 is rapid web application development toolkit designed to make your life easier.</p>
20 <ol id="getting_started_steps">
21 <li class="getting_started">
22 <h3>Code your data model</h3>
23 <p> Design your data model, Create the database, and Add some bootstrap data.</p>
24 </li>
25 <li class="getting_started">
26 <h3>Design your URL architecture</h3>
27 <p> Decide your URLs, Program your controller methods, Design your
28 templates, and place some static files (CSS and/or JavaScript). </p>
29 </li>
30 <li class="getting_started">
31 <h3>Distribute your app</h3>
32 <p> Test your source, Generate project documents, Build a distribution.</p>
33 </li>
34 </ol>
35 </div>
36 <div class="clearingdiv" />
37 <div class="notice"> Thank you for choosing TurboGears.
38 </div>
39</body>
40</html>
In order to perform our kajiki migration, we begin by creating two empty
templates. The first one will replace our master.html
, and we will call it
layout.html
:
1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3<html>
4 <py:include href="header" />
5 <py:include href="footer" />
6
7 <head>
8 <title py:block="title">Your title goes here</title>
9 <py:block name="head_content">
10 <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/style.css')}" />
11 <link rel="stylesheet" type="text/css" media="screen" href="${tg.url('/css/admin.css')}" />
12 </py:block>
13 </head>
14
15 <body>
16 <py:block name="body_header"><h1>My Header</h1></py:block>
17 <ul py:block="mainmenu" id="mainmenu">
18 <li class="first"><a href="${tg.url('/')}" class="${('', 'active')[defined('page') and page=='index']}">Welcome</a></li>
19 <li><a href="${tg.url('/about')}" class="${('', 'active')[defined('page') and page=='about']}">About</a></li>
20 <li py:if="tg.auth_stack_enabled"><a href="${tg.url('/auth')}" class="${('', 'active')[defined('page') and page=='auth']}">Authentication</a></li>
21 <li><a href="${tg.url('/environ')}" class="${('', 'active')[defined('page') and page=='environ']}">WSGI Environment</a></li>
22 <li><a href="http://groups.google.com/group/turbogears">Contact</a></li>
23 </ul>
24 <div id="content">
25 <py:if test="defined('page')">
26 <div class="currentpage">
27 Now Viewing: <span py:replace="page"/>
28 </div>
29 </py:if>
30 <py:with vars="flash=tg.flash_obj.render('flash', use_js=False)">
31 <div py:if="flash" py:content="XML(flash)" />
32 </py:with>
33 <py:block name="body_text">Your body text goes here</py:block>
34 <py:block name="bodyfooter">${footer()}</py:block>
35 </div>
36 </body>
37</html>
Note the introduction of the py:block
directive, and the disappearance of the
py:match
directives from master.html
. py:block
mimics the behavior of
Jinja2 “blocks”, providing a name to a construct in a parent template which can be
replaced by the contents of py:block
-named constructs in child templates. For
instance, the “title” slot in layout.html
:
1 <title py:block="title">Your title goes here</title>
can be replaced by a similarly-named slot in the child document index_kajiki.html
:
1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
2 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3<html py:extends="layout">
4 <py:include href="sidebars" />
5
6 <title py:block="title">Welcome to TurboGears 2.0, standing on the
7 shoulders of giants, since 2007</title>
We also provide a way of including the contents of the parent template’s slot in
a child template’s slot using ${parent_block()}
. The following slot in
layout.html
:
1 <py:block name="body_header"><h1>My Header</h1></py:block>
can be replaced in include/index_kajiki.html
with:
1 <py:block name="body_header">
2 ${parent_block()}
3 <h1>Some extra header data</h1>
4 </py:block>
Yielding the following html once rendered:
1 <h1>My Header</h1>
2 <h1>Some extra header data</h1>