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模板 变量替换 连接多行 异常 注释 代码 传递变量用户输入 表单 重复表单项 文件上传 例子 注意数据库
<!--note-->安装
可以从 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 weburls = ( "/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 " + pathapp_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 " + pathapp = 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 weburls = ( "/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 weburls = ( "/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() |
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> |
在 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() |
以下是代码片段: class index: def GET(self): title = '你好,世界' entrys = ['第一个', '第二个', '第三个'] s = web.Storage(locals()) return render.index(s) |
以下是代码片段: $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: %s/n 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') |