2010年7月12日星期一

web framework - web.py

http://webpy.org/
web.py is a web framework for python that is as simple as it is powerful.

web.py写起来简单,所以就写它了,算个引子。
况且这里本就把它当作一个拿来用,而不是拿来学习的东西。
写对性能有要求较高的服务端的话,有tornado和twisted作为选择。
而在Py的Web框架中,相比于DjanGo和TurboGears以及功能更强大的Zope/Plone,web.py则显得比较简单。
web.py资料的量不打,在其官网上有提供。tutorial用于对整体概念的学习,documentation是对模块和接口的描述,cookbook和Code samples是使用代码和代码示例,也提供有若干用于交流的途径。
其自身定位于anti-framework,意思是不去约束用户的使用方式,这样使得web.py用起来更加灵活。不过web.py的可复用的组件不够多,所以说web.py简单也是因为他将复杂度移向了其他的地方。
灵活与单薄可以算是一个权衡web.py的选择依据,也就是不论喜欢或讨厌其风格是都可以有其实用场合。
说网络应用的话php是专门的语言了,如今也有诸多框架可选择,而像drupal那样的cms,也可看作一个功能丰富类型固定的框架了。
web.py中如果再增加一些的约定的话,使用起来代码量会少一点。
以上的简短的介绍,不展开说了。

web.py的模块
当前版本0.34

部署


web.application

官网上已经简明地列出了web.py的基本用法了
import web

urls = (
'/(.*)', 'hello'
)
app = web.application(urls, globals())

class hello:
def GET(self, name):
if not name:
name = 'World'
return 'Hello, ' + name + '!'

if __name__ == "__main__":
app.run()
这段代码中涉及了URL处理,是使用正则表达匹配并捕获字符串并映射到一个命名空间中创建的类型(实现GET或POST方法,并返回要显示的字符串)上。
代码执行后默认来debug模式下,代码的修改会随即生效,也可指定web.config.debug = False。
默认使用http://127.0.0.1:8080/来通过浏览器执行效果的预览,也有提供了API的形式方便调试。
可以修改app.notfound以自定义404错误时所显示内容,创建的app也有Subdir和Subdomain的类型。


web.webapi

这部分是在Web命名空间下提供的一组函数,用于Http协议相关。
在实现GET或POST方法时会用到。

header
web.header('Content-Type', 'text/plain')
web.header("Content-Type","text/html; charset=utf-8")
输出Http头。

input
class hello:
def GET(self):
i = web.input(name = 'web')
return 'Hello, ' + web.websafe(i.name) + '!'
获得GET或POST的输入数据。

setcookie与cookies
web.setcookie('age', i.age, 3600)
参数是名称,值,有效时间。这对函数用于操作cookie。


另一组函数用户返回Http状态,使用raise语句调用。
例如
raise web.seeother("/foo")
是303重定向。
还有若干HTTPError派生的不同类型以表示不同的状态,例如NotFound(message=None)。


web.httpserver

目录static/下用于存放静态内容


web.template

首先是程序部分

render = web.template.render('templates', base='layout')
print render.hello('world')
模板文件放在'templates'目录下,可选参数base表示共用部分。
这里以'world'为参数渲染了'hello.html'文件并返回字符串结果,可以进行其他的处理之后再执行return等来使用。
通过设置globals或builtins属性(dict类型,默认None。),可以提供模板代码可以访问的变量和可以使用的函数。

另一方面则是模板代码的语法了,
它的长相是
$#hello.html
$def with (name)
$var title:"hello"
$name
$用于使用Python语句,例如访问变量(会自动web.websafe)执行函数或者for/while/if及其他语句。
$#layout.html
$def with (page)
$page.title
$:page
这里的title是子模板中定义的。


web.contrib.template

实现了对其他模板引擎的接口。


web.form

该helper模块用于渲染表单和处理验证。
signup = form.Form(
form.Textbox('username'),
form.Password('password'),
form.Password('password_again'),
validators = [form.Validator("Passwords didn't match.", lambda i: i.password == i.password_again)]
)
也可在创建Widget时指定验证条件
form.Textbox("bax", 
form.notnull,
form.regexp('\d+', 'Must be a digit'),
form.Validator('Must be more than 5', lambda x:int(x)>5)),
然后可以在控制器中或者模板中创建拷贝实例再执行.render()方法。

方法.validates()用于让form处理input,之后可以获取输入值
class hello:
def GET(self):
my_form = number_form()
return render.hello(my_form)

def POST(self):
my_form = number_form()
if not my_form.validates():
return render.hello(my_form)
else:
number = my_form['number'].value
if int(number) % 2:
return "Your number %s is odd." % number
else:
return "Your number %s is even." % number

这里的hello.html
$def with (form)
<form name="test" method="POST">
$if not form.valid: <p>Sorry, your input was invalid.</p>
$:form.render()
<input type="submit" value="Check" />
</form>
可以获得表单状态。


web.session

机制session用于保存客户端的状态
if web.config.get('_session') is None:
session = web.session.Session(app, web.session.DiskStore('sessions'), {'count': 0})
web.config._session = session
else:
session = web.config._session
在debug模式执行结果不可靠。
然后使用
class hello:
def GET(self):
session.count += 1
return "You visited " + str(session.count) + " pages."

class bye:
def GET(self):
session.kill()
return ("Bye, web!")

也可把session传递给模板
web.template.Template.globals['session'] = session

web.session的配置在web.config.session_parameters,例如timeout属性。
session可使用DiskStore或者DBStore。


web.db

这是操作数据库的一个接口,有postgresql/mysql/sqlite,需安装Py下的支持模块。
也可用sqlite3,sqlalchemy或一个文件来替换这个模块。
以下代码纯粘贴
db = web.database(dbn='postgres', user='username', pw='password', db='dbname')

entries = db.select('mytable')
db.update('mytable', where="id = 10", value1 = "foo")
db.delete('mytable', where="id=10")
sequence_id = db.insert('mytable', firstname="Bob",lastname="Smith",joindate=web.SQLLiteral("NOW()"))
results = db.query("SELECT COUNT(*) AS total_users FROM users")
print results[0].total_users
results = db.query("SELECT * FROM entries JOIN users WHERE entries.author_id = users.id")
results = db.query("SELECT * FROM users WHERE id=$id", vars={'id':10})
def post(title, body, tags):
t = db.transaction()
try:
post_id = db.insert('post', title=title, body=body)
add_tags(post_id, tags)
except:
t.rollback()
else:
t.commit()
def GET(self):
todos = db.select('todo')
return render.index(todos)
class add:
def POST(self):
i = web.input()
n = db.insert('todo', title=i.title)
raise web.seeother('/')
>>> web.db.sqlwhere({'a': 1, 'b': 2}, grouping=' OR ')
'a = 1 OR b = 2'
select方法可用的参数(传入字符串)有vars,what,where,order,group,limit,offset。
通常在整个应用中把数据库操作包装为model模块。


web.utils

该模块提供若干通用用具,在web命名空间下。
例如:
safestr转换编码
Memoize用于让函数对属于缓存一定时间的返回值
datestr将datetime对象转换为字符串
sendmail发送邮件的函数,相关配置在web.config中
safemarkdown使用Markdown标记

在这里实现了web.py的基础数据类型和相关函数,其他的在输出中可用的实用过滤器也放在这里。

web.net

这里提供了若干网络相关的组件
validaddr从字符串解析出ip地址和端口
httpdate/parsehttpdate字符串编码形式与datetime对象间的转换
urlquote将字符串转换为URL地址中可用
websafe将文本转换为网页中用于显示的UTF-8 HTML

web.http

Http相关组件,用于产生header或url。


other


这里并没有列出web.py的全部组件,并且内容以后会受到API变动的影响的。
其他还是有一些会用到的模块的:
通过web.ctx可以获得客户端信息,类型是ThreadDict。
Python的内置模块中已经有了json的支持了,做ajax时比xml用起来简单一些。
web.webopenid使用openid。
web.wsgi是WSGI组件。

web.py的使用

demo

一个最简陋的计数器
import web

urls = (
'/index.htm','index',
'/.*', 'toidx'
)
web.config.debug = False
app = web.application(urls, globals())

counter=0

class index:
def GET(self):
web.header('Content-Type', 'text/html')
global counter
counter += 1
return str(counter)

class toidx:
def GET(self):
raise web.SeeOther("/index.htm")

if __name__ == "__main__":
app.run()
这个算个引子,表示下面的内容是一些示例来着:

Guestbook,User authentication,Wiki,Blog,Test。

代码就不贴了(坑),也是想变成模块的话复用会更方便一点。
所以去用一些复用程度更高的框架的话,就可以把事情完成的更好更简单一点。


第三方组件
sqlalchemy
模板引擎
CherryPy

然后,这篇就这么简单一些吧。。。

没有评论: