web.py 模板

在 Python 中写 HTML 不是聪明的选择,相反在 HTML 中写 Python 则有趣的多。幸运的是,web.py 让这件事情做得简单而又漂亮。

创建模板

这里,我们先在项目中新建一个目录templates集中存放模板文件,便于管理。然后在templates下新建一个index.html文件,在新文件中写入如下代码:

$def with (name)

<!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
<html xmlns=”http://www.w3.org/1999/xhtml”>
<head>
    <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
    <title>Template</title>
</head>
<body>
    Hi, $name
</body>
</html>

这是一个最简单的html页面代码。

使用模板

然后在修改使用这个模板的类,比如说我们在main.py模块中的新写一个index类,入下:

class index:

    def GET(self):
        render=web.template.render(“templates”)
        return render.index(“Lisa”)

同时修改urls为:

urls = (“/”, “index”)

这样,网站默认调用index类,进而使用index.html模板,打开浏览器查看结果,页面就会打印出 Hello, Lisa 的字样。

除此之外还有两种使用模板的方法:

  • 使用frender直接指定模板文件,GET函数最后两行改为:

    render=web.template.frender(“templates/index.html”)
    return render(“Lisa”)
    
  • 直接在代码里写出模板文件,GET最后两行改为:

    template = “$def with (name)\nHello $name”
    render = web.template.Template(template)
    return render(“Lisa”)
    

模板含义

解释一下这个模板的含义:

在index.html第一行 $def with (name)表示本模板接受一个名为name的参数,也就是对应index类中return render.index(“Lisa”)中的Lisa。而render=web.template.render(“templates”)表示创建一个模板对象,模板是存放于templates目录下,然后就可以用所创建的 render 对象来访问相应的模板

比如templates目录下的index.html就是用render.index来表示(实际上是匹配寻找index.*文件,第一个匹配的就认为是所对应的模板文件),如果templates下还有个a目录,a目录下有个pagea.html,那么访问这个pagea模板就要用render.a.pagea的形式了。

页面参数

页面接收的参数可以多于一个,也可以没有,如果不需要参数,则就不需要$def with (name)这样的代码,删除掉这一句,同时修改模板中对name变量的引用,修改index类最后一句为return render.index()就可以了。

如果有参数,那么模板的第一行代码就必须是这个 $def with (…),可以多于一个参数,比如是这样$def with (gname, fname)。模板下面的那行字改为Hi, $gname $fname。同时Index类GET返回的时候赋予对应两个参数return render.index(“Lisa”,”Hayes”)。这样,页面最后显示的是打印出Hi, Lisa Hayes的字样。

另外,模板接受的这个参数也可以是一个元组,比如像下面这样:

return render.index((“Lisa”,”Hayes”))

在模板中可以如下以元组方式访问参数数据:

Hi, $name[0] $name[1]

模板语法

模板语法与python语法基本一致,主要差别可以从上面的代码中看到,要使用到$符号表明这不是文本而是模板代码。也就是每当用到程序代码、对象的时候就必须用$来与html代码和页面显示文本相区别。

对象赋值

向对象赋值时需要在$对象名之间留空格,如为一个名为vara的字符串对象赋值apple的代码为$ vara = “apple”

另外,对象赋值语句必须独占一行,前面或后面有其他代码则会程序出错。

对象引用

引用对象的时候直接使用 $+对象名的形式,如$vara

另外引用对象时还可以用{}()将对象进行明确的分组,如$(vara)s就会表示apples,如果没有括号,程序则会把 $varas作为一个整体,也就变成对varas对象的引用而发生错误。

另如果向如下定义两个数字型对象:

$varb = 1
$varc = 2

然后希望计算两个值的和,如果用$varb+$varc的形式,页面上只会得到1+2而不是3,这时也就需要把两个对象放在括号里,如$(varb+varc)的形式才能得到正确答案3。

注释

模板中支持单行注释,以$#符号开始到行末都是注释内容。

$#This is comment

注释前面可以有其他内容,但是不可以有赋值代码

如下代码是正确的:

Hi $#This is comment

但下面的则会出错:

$ vara = “apple” $#This is comment

打印$符号

由于$符号在模板中有特殊用途,所以在页面上输出$时需要进行转义操作,用连续两个$表示在页面上输出一个$符号。

Can you lend me $$50?

控制代码(循环、条件判断)

模板中支持for、while、if、elif、else,用法与在python一致,只是控制代码行要以$开始(包括break和continue命令),$开始的代码行中对象不需要在前面再加$符号,同时要注意缩进规则,如:

for 循环:

$def with (toUser,fromUser,createTime,articleCnt,articles)
<xml>
    <ToUserName><![CDATA[$toUser]]></ToUserName>
    <FromUserName><![CDATA[$fromUser]]></FromUserName>
    <CreateTime>$createTime</CreateTime>
    <MsgType><![CDATA[news]]></MsgType>
    <ArticleCount>$articleCnt</ArticleCount>
    <Articles>
        $for a in articles:
            <item>
                <Title><![CDATA[$a['title']]]></Title>
                <Description><![CDATA[$a['desc']]]></Description>
                <PicUrl><![CDATA[$a['picUrl']]]></PicUrl>
                <Url><![CDATA[$a['url']]]></Url>
            </item>
    </Articles>
</xml>

if else判断:

$if times > max:
    Stop! In the name of love.
$else:
    Keep on, you can do it.

在for循环中,有一组内置的变量可以使用,非常方便,分别如下所示:

  • loop.index: 循环次数计数 (1-开始)
  • loop.index0: 循环次数计数(0-开始)
  • loop.first: 如果是第一次循环则为True
  • loop.last: 如果是最后一次循环则为True
  • loop.odd: 如果是第奇数次循环则为True
  • loop.even: 如果是第偶数次循环则为True
  • loop.parity: 如果循环次数为奇数值为“odd” ,反之为 “even”
  • loop.parent: 本循环的外层循环对象
$for a in ["a", "b", "c", "d"]: 
    $loop.index,$loop.index0,$loop.first,$loop.last,$loop.odd,$loop.even,$loop.parity<br/>

将在页面上打印出:

1,0,True,False,True,False,odd
2,1,False,False,False,True,even
3,2,False,False,True,False,odd
4,3,False,True,False,True,even

函数-$def

函数定义也是与在python中类似,用def,只是也要在前面加$,代码也要注意$的使用和缩进:

$def hello(name=”"):
Hello $name!

函数调用也是用$加函数名的形式:

$hello(“Lisa”)

当然,定义函数时也可以与html代码混编:

$def hello(name=”"):
<strong/>Hello $name!</strong>

但是调用的时候需要在函数名前用$:前缀,否则html代码将以plain text形式打印到页面上。

$:hello(“Lisa”)

输出程序代码-$code块

如果想在模板里输入写一段python代码而又不想被$所烦恼,那么可以用到$code块

页面上输出一段代码而不希望被系统理解为模板程序代码,就需要用到$code命令,比如在模板文件中写入下面一段:

$code:
    x=10
    def print_num(num):
    return “num is %d” % num

然后再加上下面代码:

$print_num(x)
<br/>
$x

这里就用在$code块中定义的print_num函数以x变量为参数在页面上输出一行:

num is 10

然后下一行直接引用x变量,直接在页面上输出数字10。

$var

$var命令可以在模板中定义变量,在其他地方引用此模板对象的时候可以访问此定义的变量。

比如我们可以在index.html中添加如下一行:

$var vara: this is vara

表示定义了一个名为vara的变量,变量值是字符串this is vara。

把index的GET函数改为:

def GET(self):
    render=web.template.render(“templates”)
    return render.index(“Lisa”, “Hayes”).vara

那么结果显示在页面上的就是this is vara这句话。

要注意一点的是,这种变量是字符串,即便如下定义变量:

$var vara: 0

Vara也并不是数字0,

如果把GET函数最后改成:

return render.index(“Lisa”, “Hayes”).vara+1

会导致程序出错。如果希望得到期望中的结果1,则需要如下形式代码:

return int(render.index(“Lisa”, “Hayes”).vara)+1

builtins and globals

在模板中,用户可以直接使用python的内建函数和变量,写函数变量包括range, min, max 以及 True 和 False等。 除此之外,如果希望在模板中使用其他的非内建功能,就需要一点特殊操作。要在创建render的时候显式指定所需要的功能函数。

import web
import markdown

globals = {‘markdown’: markdown.markdown}
render =web.template.render(‘templates’, globals=globals)

这样,在模板中就可以用$markdown来引用markdown.markdown了。

同样,也可以用这种办法来禁用builtins

# disable all builtins
render = web.template.render(‘templates’, builtins={})

模板复用

当多个页面有着相同的结构框架的时候,为每一个页面单独维护一个模板就显得比较麻烦,web.py提供了一种简易的解决方法。

这时候就要用到创建render时使用base参数:

render=web.template.render(“templates”,base=”layout”)
return render.index(“Lisa”, “Hayes”)

这个layout表示要以templates下的layout.html模板为通用模板框架。

因此我们还要在templates目录下新建一个layout.html文件,写下如下代码:

$def with (content)
    <!DOCTYPE html PUBLIC “-//W3C//DTD XHTML 1.0 Strict//EN” “http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd”>
    <html xmlns=”http://www.w3.org/1999/xhtml”>
    <head>
        <meta http-equiv=”Content-Type” content=”text/html; charset=utf-8″ />
        <title>Layout</title>
    </head>
    <body>
        $:content
    </body>
</html>

可以看到,这个模板文件必须是有一个参数content。

然后修改index.html,只保留如下代码,其他删掉:

$def with(gname, fname)
    Hi, $(gname) $(fname)

运行程序,页面上打印Hi, Lisa Hayes,查看代码会发现最终代码就是index.html和layout.html合并在一起的结果,index.html中的内容被嵌入到layout.html中的$:content处。

在layout.html模板中还可以引用index.html中定义的var变量,这为程序带来了更多的灵活性,比如我们希望在不同的页面在使用同一个layout模板的时候能有不同的title,可以在使用layout的模板中定义如下一个var变量:

$var title:This is index.html

然后在layout.html中的title处修改为:

<title>$content.title</title>

这样,访问index.html时显示在浏览器上的title就是This is index.html,而不是原来的Layout了。

在模板中使用python代码模块

在默认状态下,在模板中是不能直接调用其他python代码模块文件中的程序的,必须做一些额外的操作。
首先,我们新建一个模块,叫module1.py,在里面写一个函数:

def hello_from_m1(name=”"):
    return “hello %s, this is module1″ % name

在main.py里导入module1:

import module1

并且修改GET函数中创建render的代码为:

def GET(self):
    render=web.template.render(“templates”,base=”layout”,globals={“m1″:module1})
    return render.index(“Lisa”)

globals参数中传递的是一个字典,key以字符串表示模块在模板中使用时的名称,value部分就是这个要在模块中使用的模块或对象的真实名称了。

最后在要使用此模块的模板中就可以用$m1来引用此模块了。比如在index.html中添加下面一行代码:

$m1.hello_from_m1(gname)

就会调用module1中的hello_from_m1函数,在页面上打印出:

hello Lisa, this is module1

在web.py模板中使用jQuery

在jQuery中$也是一个关键字,这样的话如果在模板中使用jQuery就会冲突,这时候只需要用$$做一下转义就可以了,比如:

<script type=”text/javascript”>
$$(document).ready(function()
{
    alert(“It works.”);
});
</script>

参考

推荐阅读更多精彩内容