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:blockdirectives in the child template overriding thepy:blockdirectives 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:blocksemantics 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:includebecomespy: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:matchdirectives, all of which are in amaster.htmltemplate that is being included from child templates, I recommend that you rewrite themaster.htmlaslayout.html, defining namedpy:blockregions 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:extendsdirective 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:blockdirective.
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>