Zope 页面模板
在 Web 的早期,几乎每个从事网站工作的人都是某种程序员。您可以确信,每个网站管理员都安装了他或她自己的 Web 服务器,知道如何手工编写 HTML,并且可以自行编写基本的 CGI 程序。
随着时间的推移,许多编辑、设计师和其他非程序员也参与到网站的创建中。虽然通常可以教这些人 HTML,但这种 HTML 只适用于静态网页。
动态生成的页面,当时几乎完全由 CGI 程序创建,完全是另一回事。毕竟,如果设计师想要更改静态站点的背景颜色,那么他或她可以简单地修改相应的 HTML 文件(或当今世界的全站样式表)。但是,如果这些设计位于 CGI 程序中,那么设计师必须请求程序员进行更改。这种情况对每个人都不利;设计师无法轻松地尝试新想法,程序员被迫进行小的、令人恼火的更改,而其他编程工作则在程序员进行更改时被搁置。
多年来,解决此问题的主流方案是使用模板,该模板混合了 HTML 和编程语言。也许最著名的此类模板商业实现是微软的 Active Server Pages (ASP),但 Sun 的 JavaServer Pages (JSP) 也非常流行。开源软件开发人员也开发了许多高质量的模板实现,包括 HTML::Mason(最适用于 mod_perl)、PHP(一种用于 Web 模板的编程语言)和 ADP(与 AOLServer 一起提供)。
此类模板背后的想法非常简单:一切都被假定为静态 HTML,除了放置在一组特殊括号内的内容。当使用此类模板时,设计师基本上被告知,“您可以修改所有未出现在 <% 和 %> 内部的内容。” 实际上,这通常可以很好地工作。
但是随着时间的推移,此类模板的缺点变得越来越明显。首先,当您想要循环遍历从数据库检索到的大量项目,并根据每个项目的内容以不同的背景颜色显示每个项目时,会发生什么?在这种情况下,您不能要求设计师忽略代码,因为代码和 HTML 如此交织在一起。
Zope,我们在过去几个月中一直在关注的应用程序服务器,试图使用一种称为 DTML(动态模板标记语言)的东西来解决这个问题。正如我们几个月前看到的那样,DTML 是一种具有类似 HTML 语法的编程语言,允许开发人员和设计人员 alike 创建动态页面。DTML 功能强大、灵活且易于理解(至少如果您是程序员)。
但是,如果您不是程序员,那么即使是 DTML 也可能难以理解,尤其是在它从数据库检索结果时(正如我们上个月看到的那样)。此外,HTML 编辑器不知道如何处理 DTML 标签,这可能会导致 DTML 页面被意外损坏。
由于所有这些原因,开发开源 Zope 应用程序服务器的 Zope Corporation(前身为 Digital Creations)现在鼓励开发人员关注 Zope Page Templates (ZPT),这是一种解决大多数这些问题的新模板方法。本月,我们将研究 ZPT,了解我们可以使用它来创建动态站点的方法。
ZPT 利用了三个事实:HTML 可以用 XML 语法表示,通常称为 XHTML;XML 允许您通过使用单独的命名空间来混合来自不同文档定义的标签和属性;而 WYSIWYG HTML 编辑器通常会忽略(但保留)它们无法识别的属性。
因此,ZPT 通过添加位于单独命名空间中的新属性来修改核心 HTML 语法。由于这些属性位于单独的命名空间中,因此它们是合法的 XML 和 XHTML。由于它们是属性而不是标签,因此大多数 HTML 编辑器都会忽略它们,渲染标签时就好像该属性不存在一样。并且由于 Web 浏览器会忽略它们不理解的属性,因此它们可以渲染 ZPT 页面,而无需通过 Zope。
换句话说,ZPT 使程序员可以模拟页面,以便设计师可以使用他们喜欢的任何工具来编辑和查看它。当然,模板的动态元素仅在使用 Zope 查看模板时才生效。
在 HTML 中,每个属性都有一个名称和一个值,例如,在 <a href="http://www.lerner.co.il/">Reuven's site</a> 中,属性 href 具有以下值:http://www.lerner.co.il/。名称和值用等号 (=) 分隔,值位于双引号内。当我们使用 ZPT 时,这一切都不会改变,除了属性名称由 TAL(模板属性语言)定义。TAL 定义了许多不同的可能属性名称,每个名称都指示 Zope 在显示模板时以不同的方式修改模板。
TAL 定义了属性名称,但是什么定义了属性值?为此,我们使用 TALES(TAL 表达式语法)。TALES 定义了许多数据源,包括 Python 表达式和来自周围 Python 命名空间的值。
来自 TAL 的属性名称(告诉 Zope 如何处理周围的标签)和来自 TALES 的属性值(告诉 Zope 在此 TAL 表达式中使用什么值)的组合,使我们在构建页面模板时具有惊人的灵活性。此外,TAL 和 TALES 已发布并具有开源规范这一事实意味着,如果您有它们未涵盖的特定需求,则可以对其进行添加。
现在我们已经讨论了 ZPT 背后的理论,让我们看一些例子。第一个示例是 Zope 在我们启动新页面模板时创建的骨架文档。为此,请转到 Zope 服务器上的 /manage URL,然后从右上角的“添加产品”列表中选择“页面模板”。(如果您在菜单上没有看到页面模板,则可能是 ZPT 未安装的产品。您可能需要将 Zope 副本升级到更现代的版本才能使用 ZPT。)与 Zope 世界中通常一样,然后将要求您输入此新产品的 ID(即,将出现在 URL 中的唯一字符串)。输入一个短名称,然后单击“添加并编辑”。
当您按照这些说明操作 DTML 文档或方法时,您将被带到一个包含骨架 DTML 的编辑屏幕。页面模板也是如此,只是骨架文档显然包含 TAL 和 TALES 表达式
<html> <head> <title tal:content="template/title"> The title</title> </head> <body> <h2><span tal:replace="here/title_or_id"> content title or id</span> <span tal:condition="template/title" tal:replace="template/title"> optional template id</span></h2> This is Page Template <em tal:content="template/id">template id</em>. </body> </html>
本文档可能很短,但它有效地展示了 TAL 和 TALES 以及它们在实际文档中的使用方式。以下是此骨架使用的 TAL 属性
tal:content 属性将标签的内容替换为 TALES 表达式的值。因此,在此示例文档中,单词“The title”将被 TALES 表达式 template/title 的值替换,我们将在下面讨论它。<title> 和 </title> 标签及其属性保持不变,但是当 Zope 呈现模板时,这些标签之间的文本会被更改。
tal:replace 属性类似于 tal:content,不同之处在于它会替换内容和周围的标签。这通常与 <span> 标签一起使用,该标签在任何情况下都主要用作此类标记的占位符。因此,从第一个 <span> 标签到第一个 </span> 标签的所有内容,包括标签本身,都将被 TALES 表达式 here/title_or_id 的值替换。
tal:condition 属性仅在其值为真值时才显示其内容,该值是除 0、空字符串、空列表或内置 ZPT “nothing”变量之外的任何值。
您可能会注意到,第二个 <span> 标签包含两个 TAL 属性:tal:condition 和 tal:replace。当 Zope 在给定标签中遇到多个 TAL 属性时,它首先评估定义,然后是条件,然后是重复循环,然后是内容和替换标签。(您不能在同一标签中同时拥有 content 和 replace 属性,因为它们是互斥的。)在第二个 <span> 标签中,Zope 仅在 template/title TALES 表达式为真时才插入可选的模板 ID。
除了 content、replace 和 condition 之外,TAL 还定义了另外三个属性
tal:repeat 允许我们循环遍历项目列表。如果您的模板将显示搜索结果列表、数据库中的行或文件夹中的文件,那么您可以循环遍历内容并以各种方式显示内容。
tal:attributes 允许我们替换(或添加到)封闭标签的属性。例如,我们可以在运行时通过在该 <a> 标签中添加 tal:attributes 属性来设置锚标记 (<a>) 标签的 href 属性。TALES 表达式的值将在运行时评估时用于设置该属性。
tal:define 允许您设置一个新变量,然后可以从封闭标签内访问该变量。此变量值可以传递给其他 TAL 标签或显示。
现在我们已经介绍了 TAL,让我们看一下可以分配给 TAL 属性的 TALES 表达式。
最简单的 TALES 表达式是“路径表达式”,它描述了相对于模板、其容器或请求的 Zope 对象。路径表达式类似于 URL,只是它们以名称而不是根对象 / 开头。路径表达式的最后一个元素通常是一个可以显示(使用 tal:content)、测试(使用 tal:condition)或迭代(使用 tal:repeat)的属性。
ZPT 骨架文档使用了四个 TALES 路径表达式
here/title_or_id,它将可选的“title”属性(如果存在)或强制性的“id”属性(如果没有标题)的值返回到 tal:replace 属性。
template/title,它将当前模板的(可选)标题返回到 tal:condition,然后是 tal:replace。
template/id,它返回当前模板的“id”属性。
以“template”开头的 TALES 路径表达式指的是模板对象本身,而以“here”开头的路径表达式指的是应用模板的对象——它可能是模板本身,也可能是完全不同的另一个对象。其他一些相对标记是“request”(Zope HTTP 请求对象)、“repeat”(有关当前 tal:repeat 循环的信息)、“options”(传递给模板的参数)和“container”(当前对象的文件夹)。
TALES 路径表达式很棒,但不如我们有时需要的那么灵活。因此,TALES 允许您通过以“python:”开头您的 TALES 表达式来返回 Python 代码。例如,我们可以使用以下代码在模板中包含简单计算的结果
<p>2 + 2 = <span tal:replace="python:2+2"> number</span></p>
还有一个“string:”TALES 前缀,指示该值应被视为文本字符串。字符串表达式可能包含插值变量值,方法是在变量名称前加上 $,就像在 Perl 和 shell 脚本中一样。您可以选择用花括号 ({ 和 }) 包围变量名称,如 ${x}。
到目前为止,我们所看到的对于动态内容生成非常有用。但是 DTML(或任何其他复杂的服务器端宏语言)最好的事情之一是在一个文档中定义菜单,在第二个文档中定义标题,在第三个文档中定义页脚,然后让每个页面根据需要导入它们。
处理这种情况的一种方法是在当前文件夹中创建三个单独的模板(菜单、标题和页脚),并使用 TAL 表达式(例如)导入它们
<span tal:replace="container/menu"> menu goes here</span>
TALES 查看当前模板的容器,检索菜单对象(恰好是页面模板本身),并将其内容插入到当前文档中,以代替 <span> 标签。
另一种实现 DTML 灵活性的方法是使用宏。宏在许多编程语言中都很常见,它允许我们创建在运行时扩展的功能。ZPT 宏语言称为 METAL,与 TAL 和 TALES 一样,它是在 HTML 属性中定义和调用的,放置在“metal:”XML 命名空间中。METAL 宏可以定义“插槽”或参数,参数值可以绑定到这些插槽或参数中。很容易想象您可以创建一个处理站点整体设计的宏,每个文档都适合此宏提供的插槽。更改宏定义将有效地更改整个站点的设计。
当我第一次听说 ZPT 时,我确信这又是另一种创建与其他技术和技术不兼容的模板的新方法。但是随着时间的推移,我逐渐相信 ZPT 确实是一个聪明而优雅的想法,它为开发人员和设计人员都提供了优势。虽然它不能完全替代 DTML,但我相信我现在的大部分 DTML 用法都可以用 TAL、TALES 和 METAL 的组合来替代。我期待看到这些技术随着时间的推移如何改进,以及在未来几个月和几年内它们如何更充分地集成到 Zope 中。
电子邮件:reuven@lerner.co.il
Reuven M. Lerner 是一位专门从事 Web/数据库应用程序和开源软件的顾问。他的著作《Core Perl》于 1 月由 Prentice Hall 出版。Reuven 与他的妻子和女儿住在以色列的莫迪因。