这节以及后面的习题中,你的任务是把前面创建的游戏做成网页版。这是本书的最后三个章节,这些内容对你来说难度会相当大,你要在上面花些时间才能做出来。在你开始这节练习以前,你必须已经成功地完成过了《习题 46》的内容,正确安装了 pip,而且学会了如何安装软件包以及如何创建项目骨架。如果你不记得这些内容,就回到《习题 46》重新复习一遍。
在创建你的第一个网页应用程序之前,你需要安装一个“Web 框架”,它的名字叫 lpthw.web。所谓的“框架”通常是指“让某件事情做起来更容易的软件包”。在网页应用的世界里,人们创建了各种各样的“网页框架”,用来解决他们在创建网站时碰到的问题,然后把这些解决方案用软件包的方式发布出来,这样你就可以利用它们引导创建你自己的项目了。
可选的框架类型有很多很多,不过在这里我们将使用 lpthw.web 框架。你可以先学会它,等到差不多的时候再去接触其它的框架,不过 lpthw.web 本身挺不错的,所以就算你一直使用也没关系。
使用 pip 安装 lpthw.web:
$ sudo pip install lpthw.web
[sudo] password for zedshaw:
Downloading/unpacking lpthw.web
Running setup.py egg_info for package lpthw.web
Installing collected packages: lpthw.web
Running setup.py install for lpthw.web
Successfully installed lpthw.web
Cleaning up...
以上是 Linux 和 Mac OSX 系统下的安装命令,如果你使用的是 Windows,那你只要把 sudo 去掉就可以了。如果你无法正常安装,请回到《习题 46》,确认自己学会了里边的内容。
Warning
其他 Python 程序员会警告你说 lpthw.web 只是另外一个叫做 web.py 的 Web 框架的代码分支(fork),而 web.py 又包含了太多的“魔法(magic)”在里边。如果他们这么说的话,你告诉他们 Google App Engine 最早用的就是 web.py,但没有一个 Python 程序员抱怨过它里边包含了太多的魔法,因为 Google 用它也没啥问题。如果 Google 觉得它可以,那它对你来说也不会差。所以还是回去继续学习吧,他们这些说法与其说是教导你,不如说是拿他们自己的教条束缚你,你还是忽略这些说法好了。
现在你将做一个非常简单的“Hello World”项目出来,首先你要创建一个项目目录:
$ cd projects
$ mkdir gothonweb
$ cd gothonweb
$ mkdir bin gothonweb tests docs templates
$ touch gothonweb/__init__.py
$ touch tests/__init__.py
你最终的目的是把《习题 42》中的游戏做成一个 web 应用,所以你的项目名称叫做 gothonweb,不过在此之前,你需要创建一个最基本的 lpthw.web 应用,将下面的代码放到 bin/app.py 中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | import web
urls = (
'/', 'index'
)
app = web.application(urls, globals())
class index:
def GET(self):
greeting = "Hello World"
return greeting
if __name__ == "__main__":
app.run()
|
然后使用下面的方法来运行这个 web 程序:
$ python bin/app.py
http://0.0.0.0:8080/
不过如果你执行下面的命令:
$ cd bin/ # WRONG! WRONG! WRONG!
$ python app.py # WRONG! WRONG! WRONG!
那你就错了。在所有的 python 项目中,你都不需要进到底层目录去运行东西。你应该停留在最上层目录运行,这样才能保证所有的模组和文件能被正常访问到。如果你犯了这个错误,就请回到《习题 46》学习一下关于项目布局的知识。
最后,使用你的网页浏览器,打开 URL http://localhost:8080/,你应该看到两样东西,首先是浏览器里显示了 Hello, world!,然后是你的命令行终端显示了如下的输出:
$ python bin/app.py
http://0.0.0.0:8080/
127.0.0.1:59542 - - [13/Jun/2011 11:44:43] "HTTP/1.1 GET /" - 200 OK
127.0.0.1:59542 - - [13/Jun/2011 11:44:43] "HTTP/1.1 GET /favicon.ico" - 404 Not Found
这些是 lpthw.web 打印出的 log 信息,从这些信息你可以看出服务器有在运行,而且能了解到程序在浏览器背后做了些什么事情。这些信息还有助于你发现程序的问题。例如在最后一行它告诉你浏览器试图获取 /favicon.ico,但是这个文件并不存在,因此它返回的状态码是 404 Not Found。
到这里,我还没有讲到任何 web 相关的工作原理,因为首先你需要完成准备工作,以便后面的学习能顺利进行,接下来的两节习题中会有详细的解释。我会要求你用各种方法把你的 lpthw.web 应用程序弄坏,然后再将其重新构建起来:这样做的目的是让你明白运行lpthw.web 程序需要准备好哪些东西。
在浏览器访问到你的网页应用程序时,发生了下面一些事情:
确定你真的弄懂了这些,你需要画一个示意图,来理清信息是如何从浏览器传递到 lpthw.web,再到 index.GET,再回到你的浏览器的。
第一步,把第 11 行的 greeting 变量赋值删掉,然后刷新浏览器。你应该会看到一个错误页面,你可以通过这一页丰富的错误信息看出你的程序崩溃的原因是什么。当然你已经知道出错的原因是 greeting 的赋值丢失了,不过 lpthw.web 还是会给你一个挺好的错误页面,让你能找到出错的具体位置。试试在这个错误页面上做以下操作:
你已经试过用各种方法把这个 lpthw.web 程序改错,不过你有没有注意到“Hello World”不是一个好 HTML 网页呢?这是一个 web 应用,所以需要一个合适的 HTML 响应页面才对。为了达到这个目的,下一步你要做的是将“Hello World”以较大的绿色字体显示出来。
第一步是创建一个 templates/index.html 文件,内容如下:
$def with (greeting)
<html>
<head>
<title>Gothons Of Planet Percal #25</title>
</head>
<body>
$if greeting:
I just wanted to say <em style="color: green; font-size: 2em;">$greeting</em>.
$else:
<em>Hello</em>, world!
</body>
</html>
如果你学过 HTML 的话,这些内容你看上去应该很熟悉。如果你没学过 HTML,那你应该去研究一下,试着用 HTML 写几个网页,从而知道它的工作原理。不过我们这里的 HTML 文件其实是一个“模板(template)”,如果你向模板提供一些参数,lpthw.web 就会在模板中找到对应的位置,将参数的内容填充到模板中。例如每一个出现 $greeting 的位置,$greeting 的内容都会被替换成对应这个变量名的参数。
为了让你的 bin/app.py 处理模板,你需要写一写代码,告诉 lpthw.web 到哪里去找到模板进行加载,以及如何渲染(render)这个模板,按下面的方式修改你的 app.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import web
urls = (
'/', 'Index'
)
app = web.application(urls, globals())
render = web.template.render('templates/')
class Index(object):
def GET(self):
greeting = "Hello World"
return render.index(greeting = greeting)
if __name__ == "__main__":
app.run()
|
特别注意一下 render 这个新变量名,注意我修改了 index.GET 的最后一行,让它返回了 render.index() ,并且将 greeting 变量作为参数传递给了这个函数。
改好上面的代码后,刷新一下浏览器中的网页,你应该会看到一条和之前不同的绿色信息输出。你还可以在浏览器中通过“查看源文件(View Source)”看到模板被渲染成了标准有效的 HTML 源代码。
这么讲也许有些太快了,我来详细解释一下模板的工作原理吧:
要深入理解这个过程,你可以修改 greeting 变量以及 HTML 模板的内容,看看会有什么效果。然后创建一个叫做 templates/foo.html 的模板,并且使用一个新的 render.foo 去渲染它。从这个过程你也可以看出,render 调用的函数名称只要跟 templates/ 下的 .html 文件名匹配到,这个 HTML 模板就可以被渲染到了。
我没法连接 http://localhost:8080/.
那就试试 http://127.0.0.1:8080/ 。
lpthw.web 和 web.py 有啥不同?
一样的。我只不过“锁定”了 web.py 的某个版本,把它命名为 lpthw.web ,这样同学们用的版本就都是一样的了。这样就算日后 web.py 升级升到面目全非,我也无需更新本书了。
我找不到 index.html (或者别的文件)。
很有可能是你先跑了 cd bin/ 然后才开始做项目的。不要这么做,所有的指令都应该在 bin/ 的上一层完成,所以如果你无法运行 python bin/app.py 那就说明你不在正确的目录下面。
为什么调用 template 时要写 greeting=greeting ?
这一句并不是赋值给 greeting ,而是将一个命名参数传到模板中。这也算是一种赋值,不过只会在模板函数的调用中生效。
端口 8080 无法使用。
也许是哪个杀毒软件占用了这个端口,那就换一个端口好了。
安装 lpthw.web 时出现 ImportError "No module named web" 。
很有可能是你在系统中安装了多个版本的 Python,而在这里你用了错误的一个,或者由于 pip 版本太旧导致安装没有正确完成。试着卸载并重装 lpthw.web 。如果还不行,那就再仔细检查确认自己用了正确版本的 Python。