中文版Google App Engine入门指南

郭盛
2023-12-01

转自http://lampeter123.iteye.com/blog/390536

使用webapp框架

CGI标准是很简单的,但是要把所有的代码一一写出来还是很繁重的。WEB app框架帮你解决了这些具体问题,这样你就可以将你的精力集中在你的程序的功能上了。Google App Engine支持所有用Python写的关于CGI的网站框架(包括使用CGI adaptor的 WSGI-compliant框架),包括 Django,CherryPy, Pylons, 以及 web.py.。你只需要吧这个框架的代码复制到你的程序目录下就可以使用这个框架了。

App Engine包括了一个很简单的web应用框架,叫做webapp。这个webapp框架已经在App Engine开发环境和SDK中安装好了,所以你不需要添加任何代码到你的程序中去,就可以使用这个框架了。在下面的教程中我们将使用webapp框架。

Hello, webapp!
一个 webapp 程序包含三个部分:

一个或多个 RequestHandler类用来处理请求和产生响应。
一个 WSGIApplication 实例用来根据发送请求的URL对应到相应的类
一个主程序用来运行 WSGIApplication(使用CGI adaptor)
下面让我们来把我们的欢迎辞改写成一个 webapp 程序. 编辑 helloworld/helloworld.py 文件,替换为下面的代码:
程序代码:
Python代码
1. from google.appengine.ext import webapp
2. from google.appengine.ext.webapp.util import run_wsgi_app
3.
4. class MainPage(webapp.RequestHandler):
5. def get(self):
6. self.response.headers[‘Content-Type’] = ‘text/plain’
7. self.response.out.write(‘Hello, webapp World!’)
8. application = webapp.WSGIApplication(
9. [(‘/’,MainPage)],
10. debug=True)
11. def main():
12. run_wsgi_app(application)
13. if name == “main“:
14. main()

在你的浏览器中重新加载 http://localhost:8080/ ,你将会看到改变。 (如果你关闭了web server,那么可以重新打开,方法详见 “Hello, World!”.)

webapp做了些什么呢?
这个 webapp 模块是在 google.appengine.ext 包里面的。这个模块由SDK提供,在发布版的运行环境中也会包括。

上面这段代码定义了一个request handler,MainPage,映射到根目录URL(/)。当webapp接收到一个来自URL/ 的HTTP GET请求后,它就会初始化MainPage类,然后调用这个实例的get方法。在这个方法里面,关于请求的信息可以通过self.request来获得。通常,这个方法都会设置 self.response的属性以进行响应,然后退出方法。webapp将会根据MainPage实例的生命期最后的状态发送出响应。

应用程序本身由一个 webapp.WSGIApplication 实例所代表。 参数 debug=true 将会传递给生产函数,告诉 webapp 如果在程序运行过程中遇到错误,输出堆栈调用的记录。对于产品版的程序,你可能会去掉这个参数。

函数 run_wsgi_app() 接收 WSGIApplication 实例 (或者其他 WSGI-compatible 程序对象),然后将这个程序在App Engine’s CGI environment里运行。 run_wsgi_app()和 Python标准库里提供的wsgiref模块中的WSGI-to-CGI adaptor ,但提供了一些额外的功能。比如,它可以自动检测程序是否是运行在调试环境,并且可以在调试环境中输出错误。

我们将会在下面的入门指南中使用很多webapp的功能,想要了解更多关于webapp的内容,访问:the webapp reference。

使用Google帐户服务、
Google App Engine提供了很多基于Google框架下的有用的服务,可以通过SDK中提供的类库来调用这些服务。一个很重要的服务就是用户服务,它可以让你的应用程序和Google账户用户集成,有了这个用户服务,你的用户只需要拥有Google帐号就可以登录到你的网站了。

下面我们用用户类服务来个性化我们的欢迎辞:

使用 Users类
编辑 helloworld/helloworld.py , 替换为以下代码:
Python代码
1. from google.appengine.api import users
2. from google.appengine.ext import webapp
3. from google.appengine.ext.webapp.util import run_wsgi_app
4.
5. class MainPage(webapp.RequestHandler):
6. def get(self):
7. user = users.get_current_user()
8.
9. if user:
10. self.response.headers[‘Content-Type’] = ‘text/plain’
11. self.response.out.write(‘Hello, ’ + user.nickname())
12. else:
13. self.redirect(users.create_login_url(self.request.uri))
14.
15. application = webapp.WSGIApplication(
16. [(‘/’, MainPage)],
17. debug=True)
18.
19. def main():
20. run_wsgi_app(application)
21.
22. if name == “main“:
23. main()

重新加载你的网页,你的程序将会重定向到一个本地版的Google登录界面,输入你想要的用户名,那么你的应用程序将会看到这个基于你给的用户名所创建的一个虚拟的User类对象。

当你的应用程序运行在App Engine上之后,用户将会被重定向到Google账户登录页面,然后会返回到成功登陆前或者创建用户之前用户所在的页面。

Users类的 API
让我们仔细来看看这些代码:
Python代码
1. user = users.get_current_user()

如果用户已经登录了, get_current_user() 将会返回一个 User 对象,否则,将会返回 None。
Python代码
1. if user: self.response.headers[‘Content-Type’] = ‘text/plain’ self.response.out.write(‘Hello, ’ + user.nickname())

如果用户已经登录了,将会根据用户的账户,输出一段含有用户昵称的欢迎辞。
Python代码
1. else: self.redirect(users.create_login_url(self.request.uri))

如果用户没有登录,则告诉 webapp 让它将页面重定向到Google账户登录页面。 这个重定向包含了用户所在的页面URI (self.request.uri) 所以之后将会返回到成功登陆前或者创建用户之前用户所在的页面。

用webapp处理表单
如果你希望用户自己可以留言,那么你需要一种处理用户输入信息的办法。而webapp 让数据处理变得很简单。

用webapp处理Web表单的数据
用下面的代码替换helloworld/helloworld.py:
Python代码
1. import cgi
2.
3. from google.appengine.api import users
4. from google.appengine.ext import webapp
5. from google.appengine.ext.webapp.util import run_wsgi_app
6.
7. class MainPage(webapp.RequestHandler):
8. def get(self):
9. self.response.out.write(“””
10.
11.
12.
13.


14.

15.
16.
17. “”“)
18.
19.
20. class Guestbook(webapp.RequestHandler):
21. def post(self):
22. self.response.out.write(‘You wrote:
') 
23. self.response.out.write(cgi.escape(self.request.get('content')))
24. self.response.out.write('
‘)
25.
26. application = webapp.WSGIApplication(
27. [(‘/’, MainPage),
28. (‘/sign’, Guestbook)],
29. debug=True)
30.
31. def main():
32. run_wsgi_app(application)
33.
34. if name == “ main“:
35. main()

重新加载你的程序页面,你将会看到表单,试着写点东西提交吧。

这个版本的程序有两个handler:MainPage, 映射到 URL /, 用来展示表单. Guestbook, 映射到 URL /sign, 用来展示用户提交表单的内容。

Guestbook handler 有一个 post() 方法(而不是get() 方法)。 这是因为用 MainPage 所展示的页面里用了HTTP POST方法 (method=”post”)来提交表单里的数据。如果你需要在一个类中同时使用这两个方法(post() get()),只需要各自分别定义在一个类下面就可以了。

post() 方法里的代码可以从self.request中获取表单数据,在将这些数据返回并展示给用户之前,它调用了cgi.escape()方法来去掉用户输入中的一些HTML代码标识符。 cgi是标准Python类库中的一个模块,详见the documentation for cgi。

使用数据库存储
对于一个数据量大的网站应用来说数据存储是个很有技巧的的事情。用户可能在一个特定的时间发出了一个数据请求,但是下一个时间又发出了另外一个完全不同的数据请求。所有的WEB服务都需要协调这些相互影响的请求,并且这些请求可能来自世界的各个地方。

由于有了Google App Engine,你再也不需要为这些发愁了。Google App Engine架构将为提供分布式的数据处理,解决负载平衡的问题,并且提供了API来实现所有关于数据存储的问题。

数据存储完整实例
下面是一个最新版的 helloworld/helloworld.py 代码,用来存储用户的留言。下面的所有文字都是用来解释这段代码的。
Python代码
1. import cgi
2.
3. from google.appengine.api import users
4. from google.appengine.ext import webapp
5. from google.appengine.ext.webapp.util import run_wsgi_app
6. from google.appengine.ext import db
7.
8. class Greeting(db.Model):
9. author = db.UserProperty()
10. content = db.StringProperty(multiline=True)
11. date = db.DateTimeProperty(auto_now_add=True)
12.
13. class MainPage(webapp.RequestHandler):
14. def get(self):
15. self.response.out.write(‘’)
16.
17. greetings = db.GqlQuery(“SELECT * FROM Greeting ORDER BY date DESC LIMIT 10”)
18.
19. for greeting in greetings:
20. if greeting.author:
21. self.response.out.write(‘%s wrote:’ % greeting.author.nickname())
22. else:
23. self.response.out.write(‘An anonymous person wrote:’)
24. self.response.out.write(‘

%s
’ %
25. cgi.escape(greeting.content))
26.
27. # Write the submission form and the footer of the page
28. self.response.out.write(“””
29.
30.

31.

32.
33.
34. ”“”)
35.
36. class Guestbook(webapp.RequestHandler):
37. def post(self):
38. greeting = Greeting()
39.
40. if users.get_current_user():
41. greeting.author = users.get_current_user()
42.
43. greeting.content = self.request.get(‘content’)
44. greeting.put()
45. self.redirect(‘/’)
46.
47. application = webapp.WSGIApplication(
48. [(‘/’, MainPage),
49. (‘/sign’, Guestbook)],
50. debug=True)
51.
52. def main():
53. run_wsgi_app(application)
54.
55. if name == “ main“:
56. main()

将上面的代码替换掉 helloworld/helloworld.py 原有的代码,然后重新加载 http://localhost:8080/ 试着发布一条留言,看看你的留言是否被正确地存储并且正常地显示了。

存储用户提交的留言

App Engine 包含了一个基于Python的数据存储模型. 这个模型类似于 Django’s data modelling API, 但是使用了Google自己的存储环境.

对于上一章实现的留言程序,我们想要把用户提交的留言保存起来,每个留言都包含作者名称,消息内容,发布时间等等,并且按照留言的先后将其显示出来。

为了使用data modeling API,在代码顶部添加 google.appengine.ext.db 模块:
Python代码
1. from google.appengine.ext import db

下面的这段代码定义了一个用来存储用户留言的模块:
Python代码
1. class Greeting(db.Model):
2. author = db.UserProperty()
3. content = db.StringProperty(multiline=True)
4. date = db.DateTimeProperty(auto_now_add=True)

这段代码定义了 Greeting 模型的三个属性: author 是一个 User 对象, content 是一个字符串对象,anddate 是一个datetime.datetime对象。

其中一些属性包含了默认值:比如db.StringProperty 类型中 multiline=True 表明该字符串中可以包含换行符;db.DateTimeProperty类型中 auto_now_add=True 表明当Greeting对象创建的时候,将使用当前时间初始化这个属性。关于数据模型的属性的更多帮助,请查看 the Datastore reference。

既然我们已经定义了一个数据对象模型,接下来,我们创建一个Greeting对象,并且把它保存起来。编辑 Guestbook handler :
Python代码
1. class Guestbook(webapp.RequestHandler):
2. def post(self):
3. greeting = Greeting()
4.
5. if users.get_current_user():
6. greeting.author = users.get_current_user()
7.
8. greeting.content = self.request.get(‘content’)
9. greeting.put()
10. self.redirect(‘/’)

这个新的 Guestbook handler 创建了一个新的 Greeting 对象,然后根据用户提交的数据设置 author 和 content 的属性值。 它并没有甚至 date 的值,所以 date 会自动设成当前时间,因为我们在模型建立的时候已经设置了。

最后一行, greeting.put() 将新创建的对象保存进数据库,如果put()进去的是从数据库中提取的对象,put() 会更新那条数据记录,而现在我们是新创建的一个对象,所以 put() 会添加一条新的记录到数据存储里。

使用 GQL 获取数据记录
App Engine datastore 使用了一套复杂的数据储存系统.但是它并不是一个标准的关系数据库,所以不能使用标准的Sql语句进行查询。作为一个替代,Google准备了一套类Sql的查询语句,称之为GQL.GQL 提供了和SQL基本类似的语法来读取数据。

下面是新版的 MainPage handler 代码,用来查询数据库中的所有留言。
Python代码
1. class MainPage(webapp.RequestHandler):
2. def get(self):
3. self.response.out.write(‘’)
4.
5. greetings = db.GqlQuery(“SELECT * FROM Greeting ORDER BY date DESC LIMIT 10”)
6.
7. for greeting in greetings:
8. if greeting.author:
9. self.response.out.write(‘%s wrote:’ % greeting.author.nickname())
10. else:
11. self.response.out.write(‘An anonymous person wrote:’)
12. self.response.out.write(‘

%s
’ %
13. cgi.escape(greeting.content))
14.
15. # Write the submission form and the footer of the page
16. self.response.out.write(“””
17.
18.

19.

20.
21.
22. ”“”)

查询语句出现在这一行:
Python代码
1. greetings = db.GqlQuery(“SELECT * FROM Greeting ORDER BY date DESC LIMIT 10”)

或者,你也可以在Greeting类里面调用 gql(…) 方法,那样就不必使用 SELECT * FROM Greeting 这样的查询语句了:
Python代码
1. greetings = Greeting.gql(“ORDER BY date DESC LIMIT 10”)

和SQL语句类似,关键字 (比如 SELECT) 是大小写无视的,字段名是区分大小写的。

要注意的是,GQL语句总是返回完整的对象,所以GQL查询语句不能指定要查询的字段名。也就是说,所有的GQL语句都是以SELECT * FROM model 开头的。

一个GQL查询语句可以用 WHERE指定查询条件,你可以指定一个或多个条件。 和SQL不同的是,GQL查询不能包含变量值:GQL使用参数绑定查询中所有的变量.例如 , 获取当前登录用户的留言:
Python代码
1. if users.get_current_user():
2. greetings = Greeting.gql(“WHERE author = :1 ORDER BY date DESC”,
3. users.get_current_user())

你也可以使用命名参数代替之:
Python代码
1. greetings = Greeting.gql(“WHERE author = :author ORDER BY date DESC”,
2. author=users.get_current_user())

另外, Google datastore API 还提供了另外一种获取数据的方法::
Pyton代码
1. greetings = Greeting.all()
2. greetings.filter(“author =”, users.get_current_user())
3. greetings.order(“-date”)

想了解 GQL查询语法的更多内容, 请查看 the Datastore reference。

清空开发版服务器中的数据存储
为了方便你测试自己的应用,GAE开发环境使用了一个临时文件来保存本地的数据,要清空本地开发环境的数据,可以使用如下的命令行:
dev_appserver.py –clear_datastore helloworld/

使用模板
HTML里面嵌入在编程代码里是非常杂乱的,所以我们最好使用一个独立的文件来专门处理HTML代码,以便于将界面显示和数据获取的过程相互独立出来。有很多使用Python实现的模板系统,比如: EZT, Cheetah,ClearSilver, Quixote, Django 等等.你可以选择这里面的任意一个。

为了大家方便, webapp 模块默认包含了Django的模板系统.Django模板是Google App Engine的一部分,所以你不需要单独进行绑定就可以直接使用。

使用 Django 模板
首先在 helloworld/helloworld.py中引入template模块:
Python代码
1. import os
2. from google.appengine.ext.webapp import template

重新编写 MainPage handler:
Python代码
1. class MainPage(webapp.RequestHandler):
2. def get(self):
3. greetings_query = Greeting.all().order(‘-date’)
4. greetings = greetings_query.fetch(10)
5.
6. if users.get_current_user():
7. url = users.create_logout_url(self.request.uri)
8. url_linktext = ‘Logout’
9. else:
10. url = users.create_login_url(self.request.uri)
11. url_linktext = ‘Login’
12.
13. template_values = {
14. ‘greetings’: greetings,
15. ‘url’: url,
16. ‘url_linktext’: url_linktext,
17. }
18.
19. path = os.path.join(os.path.dirname(file), ‘index.html’)
20. self.response.out.write(template.render(path, template_values))

最后, 在 helloworld 的目录下面创建一个新文件index.html, 内容如下:
Html代码
1.
2.
3. {% for greeting in greetings %}
4. {% if greeting.author %}
5. {{ greeting.author.nickname }} wrote:
6. {% else %}
7. An anonymous person wrote:
8. {% endif %}
9.

{{ greeting.content|escape }}

10. {% endfor %}
11.
12.
13.

14.

15.
16.
17. {{ url_linktext }}
18.
19.
20.

刷新浏览器,然后查看网页上的变化。

template.render(path, template_values) 接受输入一个文件路径和一个dictionary类型的数据字典,并输出一段经过处理的文本。这个模板使用了Django模板的语法.在模板文件中可以直接使用引入的值,并且可以使用这些对象的属性。在许多实际例子里,你可以直接使用GAE数据模型,并访问他们的属性。

提示: App Engine 应用程序对所有上传的文件的访问权限都是只读的,所以对文件的写操作是被禁止的;当前工作路径是应用程序的根目录,所以 index.html 的路径可以简单写成”index.html”。

想要了解更多关于Django模板引擎的内容,参见 the Django 0.96 template documentation。

使用静态文件
和其他的web发布环境不同,Google App Engine 不支持直接将应用目录下的文件直接输出的功能。也就是说,如果我们将模板文件取名为 index.html, 我们并不能直接通过URL /index.html来访问这个文件。但是现在有非常多的应用需要我们提供文件直接输出的功能,例如图片,CSS,JavaScript等等,这些类型的文件都需要直接输出到客户端。GAE提供了这样的功能,你不需要编写自己的处理模块来进行额外的处理。

使用静态文件
编辑helloworld/app.yaml 修改里面的代码:
Html代码
1. application: helloworld
2. version: 1
3. runtime: python
4. api_version: 1
5.
6. handlers:
7. - url: /stylesheets
8. static_dir: stylesheets
9.
10. - url: /.*
11. script: helloworld.py

新加的handlers 部分定义了两个URL处理模块,其中 /stylesheets开头的所有URL都定义并转向了静态文件夹stylesheets ,如果在这个文件夹中发现了请求的文件,就会直接把这个文件的内容返回给客户端;而其他请求都会由helloworld.py 脚本进行处理。

默认情况下,App Engine 按照文件名后缀处理静态文件,如 .css 结尾的文件就会使用MIME类型text/css。

GAE按照在 app.yaml定义的顺序对URL进行处理。在这个例子里 /stylesheets 将先于 /.* 对路径进行处理。

想要了解更多在 app.yaml中的选项,请查看 the app.yaml reference。

下面,我们创建 helloworld/stylesheets目录,并且在这个目录下创建一个新文件 main.css :
Xml代码
1. body {
2. font-family: Verdana, Helvetica, sans-serif;
3. background-color: #DDDDDD;
4. }

最后,编辑 helloworld/index.html 插入如下几行:
Html代码
1.
2.
3.

刷新并查看效果,新版本的程序使用了样式表。

上传你的程序
用户创建和管理GAE应用程序,都需要使用GAE控制台程序来进行。其中,用于上传的命令行工具叫做appcfg.py。

注意: 到目前为止,还没有办法删除已经发布的应用。这个功能会在稍后提供。在现在的预览测试阶段,每个用户可以注册3个应用程序ID,如果你不想使用你自己的应用程序ID,你可以仅仅读一下这一章,到你真正想发布自己的应用的时候再尝试。

注册应用程序
访问http://appengine.google.com/,使用你的Google帐号登录到App Engine管理平台。(如果你还没有Google帐号,请先申请一个)。

为了创建一个新的GAE应用,请点击按钮 “Create an Application” ,按照提示注册应用程序ID,应用程序ID的名字必须是唯一的。创建ID后,你就可以拥有一个http://application-id.appspot.com/这样的URL地址来访问你的WEB应用了.当然,如果你拥有自己的域名的话,也可以将其绑定到你自己的应用。

修改 app.yaml , 吧 application: 的值设置为你刚刚申请的应用程序ID。

上传你的Application
上传你的程序,使用下面的命令:

appcfg.py update helloworld/

按照提示,输入您自己的Google 用户名和密码。

现在你已经可以使用如下地址访问您刚刚上传的WEB应用了:

http://application-id.appspot.com

恭喜你!

 类似资料: