月度归档:2011年04月

Google App Engine上如何使用webpy

感觉webpy很轻量级,于是想以它为框架开发web应用。碰到的第一个问题,就是怎么在GAE(google app engine)中调用webpy,也就是import web 怎样才能成功。

由于python、webpy、GAE 都是0基础,所以在gae 的main.py中直接写import web 报错后,就找不到解决方法了。中间也尝试了将web.py目录拷贝到谷歌的lib目录以及当前目录,但由于完全是盲目的尝试,最终都没有成功。

今天静下心来,想解决这个问题,于是就在google上找答案,终于知道是要将web.py的那个“web”目录放到项目的根目录中。然后按部就班的做,拷贝例子,最后重新编译下:python web/template.py –compile templates 就ok了。

具体见原文地址:
http://hi.baidu.com/smallfish_xy/blog/item/f5c444fbcef1ebd7b58f3119.html

web.py 0.3教程

web.py 0.3 教程

web.py 是一个漂亮的 Python web 框架,当前最新版本是 0.3,增加了 sessions、subdir_application 和 subdomain_application 以及更好的数据库支持。

目录:

  • 安装
  • 第一个程序
  • URL 处理
    • 正则映射
    • 自动映射
  • 开发
  • 静态文件
  • 重定向
  • 多个应用程序
    • 基于子目录的应用程序
    • 基于子域名的应用程序
  • 测试
    • doctest
    • unittest
  • 会话
    • session
    • cookies
    • 设置
    • 读取
  • 发邮件
    • 用 gmail 发邮件
  • 获取客户端信息
    • web.ctx
    • 例子
    • ctx 里的数据
    • Request
    • Response
  • 模板
    • 变量替换
    • 连接多行
    • 异常
    • 注释
    • 代码
    • 传递变量
  • 用户输入
    • 表单
    • 重复表单项
    • 文件上传
    • 例子
    • 注意
  • 数据库

安装

可以从 github.com/webpy/webpy 里获得最新的 web.py

执行下列命令抓取

git clone git://github.com/webpy/webpy.git

git clone git://github.com/webpy/webpy.git

之后可以直接将抓回来的 webpy/web/ 文件夹拷到 web.py 程序目录里使用,或者执行下列命令将 web.py 安装到系统里

cd webpy

python setup.py build

sudo python setup.py install

此外,也许您还需要安装 python-cheetah, python-flup

第一个程序

第一个程序就以 你好,世界 来开始吧

#!/usr/bin/python

# -*- coding: UTF-8 -*-

import web

# URL 规则

urls = (

‘/(.*)’, ‘hello’

)

# 应用程序

app = web.application(urls, globals())

class hello:

def GET(self, name):

if not name: name = ‘world’

web.header(‘Content-Type’, ‘text/html; charset=UTF-8′)

return ‘你好,’ + 世界 + ‘!’

# 启动

if __name__ == “__main__”:

app.run()

将他保存为 code.py,在命令行执行

python code.py

现在这个 web.py 应用程序就运行了一个 web 服务器默认监听 8080 端口了,在浏览器中访问 http://0.0.0.0:8080/,应该就可以看到 “您好,世界!” 了。

在命令行的 code.py 后加参数 “IP 地址:端口” 来控制 web 服务器的监听范围

比如监听 8000 端口

python code.py 0.0.0.0:8000

或者,只接受 192.168.1.25 来的 8086 端口

python code.py 0.0.0.0:8000


URL 处理

web.py 0.3 有两种方式处理 URL,有 urls 指定,或由元类自动映射

正则映射

web.application() 用 urls 将路径的正则匹配映射到元类

import web

urls = (

“/hello”, “hello”,

“/magic/.*”, “magic”

)

app = web.application(urls, globals())

自动映射

web.auto_application() 类似于 web.application() ,但自动根据元类来构造 urls

import web

app = web.auto_application()

class hello(app.page):

def GET(self):

return “hello, world!”

开发

静态文件

重定向

多个应用程序

web.py 支持集成多个应用程序,或说将应用划分到多个子应用程序。

比如将 /blog 分出在 blog.py 里边

在 blog.py:

import web

urls = (

“”, “reblog”,

“/(.*)”, “blog”,

)

class reblog:

def GET(self): raise web.seeother(‘/’)

class blog:

def GET(self, path):

return “blog ” + path

app_blog = web.application(urls, locals())

在 code.py:

import web

import blog

urls = (

“/blog”, blog.app_blog,

“/(.*)”, “index”,

)

class index:

def GET(self, path):

return “hello ” + path

app = web.application(urls, locals())

基于子目录的应用程序

可以根据子目录来划分应用程序,比如将 wiki.py , blog.py , auth.py 分出来

import web

import wiki

import blog

import auth

mapping = (

“/wiki”, wiki.app,

“/blog”, blog.app,

“/auth”, auth.app,

)

app = web.subdir_application(mapping)

基于子域名的应用程序

也可以根据子域名来划分应用程序,这可以方便做 virtual hosting, 比如www.example.com (和 example.com)是 mainsite , XXXX.example.com 是 usersite

import web

import mainsite

import usersite

mapping = (

“(www.)?example.com”, mainsite.app,

“.*.example.com”, usersite.app,

)

app = web.subdomain_application(mapping)

测试

doctest

import web

urls = (

“/hello”, “hello”,

)

app = web.application(urls, globals())

class hello:

“””Hello world example.

>>> response = app.request(“/hello”)

>>> response.data

‘hello, world!’

>>> response.status

’200 OK’

>>> response.headers[‘Content-Type’]

‘text/plain’

“””

def GET(self):

web.header(‘Content-Type’, ‘text/plain’)

return “hello, world!”

unittest

import unittest

from helloworld import app

class HelloWorldTest(unittest.TestCase):

def testHelloWorld(self):

response = app.request(‘GET’, ‘/’)

self.assertEquals(response.data, ‘hello, world!’)

self.assertEquals(response.headers[‘Content-Type’], ’text/plain’)

self.assertEquals(response.status, ’200 OK’)

if __name__ == “__main__”:

unittest.main()

会话

session

web.py 0.3 正式提供了基于 cookie 的 session 支持。

import web

urls = (

“/count”, “count”,

“/reset”, “reset”

)

app = web.application(urls, locals())

session = web.session.Session(app, web.session.DiskStore(‘sessions’), initializer={‘count’: 0})

class count:

def GET(self):

session.count += 1

return str(session.count)

class reset:

def GET(self):

session.kill()

return “”

if __name__ == “__main__”:

app.run()

上边 Session 的 initializer 参数可以是 dict 或 func,用来初始化 session。

web.py 的 session 存储有基于文件的 DiskStore 和基于数据库的 DBStore ,上边例子是 DiskStore。

也可以使用基于数据库的 DBStore。

使用 DBStore session 前需要在数据库建表

create table sessions (

session_id char(128) UNIQUE NOT NULL,

atime datetime NOT NULL default current_timestamp,

data text

);

然后就可以

db = web.database(dbn=’postgres’, db=’mydatabase’, user=’myname’, pw=”)

store = web.session.DBStore(db, ‘sessions’)

session = web.session.Session(app, store, initializer={‘count’: 0})

cookies

设置

可以用 web.setcookie() 、web.cookies() 来设置和读取 cookies

参数:

web.setcookie(name, value, expires=””, domain=None, secure=False)

name (string) – The actual name of the cookie, as stored by the browser, and returned to the server.

value (string) – The value you want stored under that name.

expires (int) – Optionally, is the time in seconds until the browser should expire the cookie. Note: this must be an integer, not a string.

domain (string) – The domain the cookie is valid for. By default, set to the host accessed, this allows you to set the domain, rather than just a host (such as .webpy.org).

secure (bool)- If True, require that the cookie only be sent over HTTP/S.

例子

class CookieSet:

def GET(self):

i = web.input(age=’25′)

web.setcookie(‘age’, i.age, 3600)

return “Age set in your cookie”

读取

有两个方法读取 cookie

1. 当 cookie 不存在时抛出异常

web.cookies().get(cookieName)

#cookieName is the name of the cookie submitted by the browser

2. 有预设值,避免异常

foo = web.cookies(cookieName=defaultValue)

foo.cookieName # return the value (which could be default)

#cookieName is the name of the cookie submitted by the browser

例子:

通过 web.cookies() 来访问

如果先前 web.setcookie() 设置了 age , 那可以这样读取

class CookieGet:

def GET(self):

c = web.cookies(age=”25″)

return “Your age is: ” + c.age

上边的当没有 cookie 时会异常,如果要考虑没有 cookie 的情况,可以类似下边这样:

class CookieGet:

def GET(self):

try:

return “Your age is: ” + web.cookies().get(‘age’)

except:

# Do whatever handling you need to, etc. here.

return “Cookie does not exist.”

发邮件

用 gmail 发邮件

先用 web.config 配置 smtp

web.config.smtp_server = ‘smtp.gmail.com’

web.config.smtp_port = 587

web.config.smtp_username = ‘cookbook@gmail.com’

web.config.smtp_password = ‘secret’

web.config.smtp_starttls = True

再用类似下边的发邮件

web.sendmail(‘cookbook@gmail.com’, ‘user@example.com’, ‘subject’, ‘message’)

或者可以附上邮件头

web.sendmail(‘cookbook@webpy.org’, [‘user@example.com’, ‘user2@example.com’],

‘subject’, ‘message’,

cc=’user1@example.com’, bcc=’user2@example.com’,

headers=({‘User-Agent’: ‘webpy.sendmail’, ‘X-Mailer’: ‘webpy.sendmail’,})

)

获取客户端信息

web.ctx例子

class example:

def GET(self):

referer = web.ctx.env.get(‘HTTP_REFERER’, ‘http://google.com’)

useragent = web.ctx.env.get(‘HTTP_USER_AGENT’)

raise web.seeother(referer)

ctx 里的数据

Request

environ a.k.a. env — a dictionary containing the standard WSGI environment variables

home — the base path for the application http://example.org

homedomain — ??? http://example.org

homepath — ???

host — the domain requested by the user example.org

ip — the IP address of the user xxx.xxx.xxx.xxx

method — the HTTP method used GET

path — the path requested by the user /articles/845

protocol — the protocol used https

query — an empty string if there are no query arguments otherwise a ? followed by the query string ?foo=amorphous&bar=blasphemous

fullpath a.k.a. path + query — the full path requested including query arguments /articles/845?foo=amorphous&bar=blasphemous

Response

status — the HTTP status code (default ’200 OK’) 401 Unauthorized

headers — a list of 2-tuples containing HTTP headers

output — a string containing the response entity

模板

web.py 支持模板(注意需要 python-cheetah)

先看看将 第一个程序 改为使用模板

写入 templates/hello.html :

$def with (name, todos={})

$if name:

<h1>你好,$name!</h1>

$else:

<h1>你好,世界!</h1>

注意模板文件首行要个 $def with() ,

在 code.py 里用

render = web.template.render(‘templates/’)

class hello:

def GET(self, name):

return render.hello(name)

变量替换

Look, a $string.

Hark, an ${arbitrary + expression}.

Gawk, a $dictionary[key].function(‘argument’).

Cool, a $(limit)ing.

Stop, $money isn’t evaluated.

We use basically the same semantics as (rejected) PEP 215. Variables can go anywhere in a document.

连接多行

If you put a backslash

at the end of a line

(like these)

then there will be no newline.

这会输出一整行

异常

Here are some expressions:

$for var in iterator: I like $var!

$if times > max:

Stop! In the name of love.

$else:

Keep on, you can do it.

$try:

$get(input)

$except:

Couldn’t find it.

That’s all, folks.

注释

$# Here’s where we hoodwink the folks at home:

Please enter in your deets:

CC: [ ] $#this is the important one

SSN: $#Social Security Number#$ [ ]

$# 到行末的是注释

代码

可以将 python 语句放在行首的 “$ ” 后(从”$ ” 开始,之道行尾,或下一个 “$” )

$def with()

$ mapping = dict(a=[1, 2, 3],

$ b=[4, 5, 6])

<b>$mapping[‘a’][0]</b>

传递变量

可以这样设置模板里的全局变量

len = ‘cn’

web.template.Template.globals[len] = len

或者直接传递 globals()

web.template.Template.globals = globals()

也可以在 metaclass.func 里传递 locals()

class index:

def GET(self):

title = ‘你好,世界’

entrys = [‘第一个’, ‘第二个’, ‘第三个’]

s = web.Storage(locals())

return render.index(s)

而 templates/index.html 里用

$def with(s)

…<title>$s.title</title>

<ul>

$for entry in s.entrys:

<li>$entry</li>

</ul>

用户输入

表单

重复表单项

复选框有重复的表单项,譬如 http://example.com?id=10&id=20 这样的请求,可以用类似下边代码获得多个 id 项值

class SomePage:

def GET(self):

user_data = web.input(id=[])

return “<h1>” + “,”.join(user_data.id) + “</h1>”

文件上传例子

import web

urls = (‘/upload’, ‘Upload’)

class Upload:

def GET(self):

return “””<html><head></head><body>

<form method=”POST” enctype=”multipart/form-data” action=””>

<input type=”file” name=”myfile” />

<br/>

<input type=”submit” />

</form>

</body></html>”””

def POST(self):

x = web.input(myfile={})

return “filename: %sn value: n%s” % (x[‘myfile’].filename, x[‘myfile’].value)

if __name__ == “__main__”:

app = web.application(urls, globals())

app.run()

注意

form 表单必须有属性 enctype=”multipart/form-data”,否则文件上传会不正常。

在 webpy 代码里,web.input() 参数必须有默认值(如上边的 myfile={} )。否则 web.input 不能获取文件名,而只能获取文件内容。

数据库

web.py 0.3 有更好的 db 处理

import web

db = web.database(dbn=’postgres’, db=’todo’, user=’you’, pw=”)

db.select(‘todo’)

db.select(‘todo’, where=’id=$id’, vars={‘id’: 2})

db.query(‘SELECT * FROM todo’)

Tags: , ,