与Web2Py共舞

地址:[url]http://www.python-cn.cn[/url]也许你已经听过说web2py,它是Web开发框架中的新成员。web2py使用Python进行编写,所以它很可靠并且比Ruby on Rails快。web2py本身也是一个web应用,所以你可以通过浏览器对你的应用程序进行所有的开发、部署和维护,而这种方式使得它比其它任何框架都易于使用。除此之外,web2py被打成一个完整的包(可用于Windows, Mac或Unix/Linux),同时包含了开发所需要的一切(包括Python, SQLite3, 和多线程web服务器). [译注: 现在是cherrypy]




你可以从这里得到web2py: http://www.web2py.com 。这篇文档在设计时有意模仿了 http://onlamp.com/pub/a/onlamp/2005/01/20/rails.html 。这样你就可以同Rails进行比较了。

2


什么是Python?
Python是一种面向对象的编程语言,被设计得超级容易教学,并且在功能上没有任何打折。绝大部分Java算法都可以用Python来重写,而长度仅为原来的二十分之一。Python自带了一整套可移植的库,包括对许多标准互联网协议(http, xml, smtp, pop, 和imap,只提到了几个)的支持和对操作系统API的支持。




什么是web2py?
web2py是使用Python编写的一个开源web框架,并可以使用Python进行数据库驱动的web 应用方面的快速编程。如今有许多的web框架,包括Ruby on Rails, Django, Pylons和 Turbo Gears,所以为什么又开发一个呢?我是在心中带着下面的目标进行web2py的开发的:




尽可能象Rails, 但是用Python来开发,这样可以更稳定和更高效。

1



一体化的包,不需要安装、无配置和不需要shell脚本。







超级容易教学(我的工作是教学)。所以我把web2py本身也做成了一个web应用程序。







从上到下的设计,这样web2py的API从头一天开始就是稳定的。







眼见为实
web2py编程象Rails编程一样容易,但如果你既不会Python也不会Ruby,web2py学起来要比Rails容易多了。




最重要的是,与同等功能的J2EE或PHP相比,web2py所需的代码量要少,同时它强迫你使用一种非常好并且安全的编程习惯。




web2py阻止目录遍历,SQL注入攻击(SQL injection),跨站脚本执行(cross site scripting),和回复攻击弱点(reply attack vulnerability)。




web2py替你对session,cookie和应用错误进行管理。所有应用错误都会生成一个ticket发送给用户,并且会为管理员生成一条日志项。




web2py会为你编写所有的SQL。它甚至可以创建表并决定何时执行一个数据库迁移的动作。




试一下吧。




软件安装
访问 http://mdp.cti.depaul.edu/examples 并下载Windows, Mac或Unix文件。




如果你选择使用Windows或Mac版本,你只需要执行:unzip文件,然后分别点击web2py.exe或wweb2py.app。




如果你选择使用Unix版本,你需要安装Python解释器(版本2.4或更高)和SQLite3数据库。 [译注:2.4与2.5有些区别。在2.5中使用sha512的摘要算法,而2.4只使用sha。同时2.5内置了sqlite,因此不需要安装。如果有加密数据,则2.4与2.5下的版本处理可能有不同,需要注意。] 有了这些之后,unzip web2py然后运行




python web2py.py在生产配置中,你应该使用PostgreSQL或MySQL而不是SQLite3。从web2py的角度来看,修改配置象修改程序中的一行代码一样简单,不过在这里不讨论它,因为开发中你不需要关心它。




运行web2py
在启动时,web2py会问一个问题: “choose the administrator password”,选择一个。在那之后,web2py会替你打开一个web浏览器(记住未曾输入过命令!),同时显示这个欢迎页面

2



点击 “administrative interface”





然后输入在启动时选择的口令。你应该被重定向到管理界面的”site”页面:





这里你可以:




安装和反安装应用程序





创建和设计(编程)你的应用程序







清理错误日志和session







以字节码方式编译应用程序用来分发和快速执行







web2py自带了三个应用程序: admin (管理界面本身), examples (交互文档), 和 welcome (可用来创建其它应用程序的基础模板)。




开始写代码
我们将创建一个在线的协作方式的 cookbook ,用来保存和分享菜谱。我们想让我们的食谱书有以下功能:




显示所有菜谱的清单。





创建新菜谱和编辑存在的菜谱。







给菜谱设定 category (象”dessert”(餐后甜点)或”soup”(汤))。







如果需要,你可以下载完整的web2py Cookbook例子并且跟着做。




创建一个空的web2py应用程序
为创建一个新的应用,在合适的字段 [译注:create new application] 上输入一个名字 (在本例中为cookbook),然后点击Submit按钮:





一个新的web2py应用程序不是空的,它是welcome应用程序的克隆。它包含单个controller(控制器),单个view(视图),基本layout(布局),通用view和称为appadmin(不要与admin混了,admin 是整个站点的管理界面)的数据库管理界面。




测试空的Web应用程序
你已经运行了web2py web服务器,所以其实没有什么可以测试的。不管怎么样,在 cookbook/design 上点击,你会看到





在这里你可以查看/创建/编辑你的应用程序组件。在 Controllers 下,有一个叫做 default.py 的文件,带有 “exposes index”。如果你在 index 上点击,你创建的新应用程序会 “welcome you”




web2py Model(模型) View(视图) Controller(控制器) Design(设计)
任何web2py应用程序由以下内容组成:




Models: 用来描述你的应用程序的数据存储的文件。例如,你的数据库表中的字段、它们的关系和要求。web2py会告诉你在每个model文件已经定义了哪些表。 [译注: model.tables?]





Controllers: 包含你的应用程序处理逻辑的文件。每个URL唯一映射到一个controller 文件的一个函数上。这个函数可以生成页面、委派一个view来渲染一个页面、重定向到另一个URL或引发一个异常(根据异常的不同,有可能会产生一个ticket或出现在一个 HTTP错误页面中)。web2py会告诉你每个controller文件所暴露出来的函数。 [译注:如果在controller中的函数使用``__``开始,如``def __init()``,它将是一个私有函数,不会被暴露出来,符合Python定义的习惯。]







Views: 包含HTML和特殊的 {{ }} 标签的文件。这些标签可以在由controller中返回的变量中进行渲染。这是你的应用程序的展现层。web2py会告诉你何时一个view从其它的 view中进行扩展(extend)或导入。







Languages: 包含所有你想要支持为其它任一语言的字符串的翻译列表的文件。这些字符串需要你明确标识为语言依赖。 [译注:在需要翻译的字符串使用T()函数进行封装。不过目前好象只能用在controller, view, model中。对于其它的模块好象还不支持。]







Static files: 其它的所有文件,包括图片、CSS、Javascript,等等。







注意,你既不需要一个编辑器,也不需要知道web2py的目录结构,因为你可以通过design 页面来创建和编辑。 [译注:web2py自带的web编辑器可以很好的支持语法高亮,包括 Python。不过对于某些静态文件,如Javascript不能进行修改,希望在以后可以改进。]




还要注意,为每个controller函数(在Rails中叫action)给定一个view是一种好的习惯,不过不一定非要如此,因为当没有指定view( [译注:可以简单理解为模板] )时,web2py 会总是使用 generic.html view来渲染任何页面。




URL和Controller
这张图展示了web2py核心功能的通用结构





一个类似:




http://hostname/cookbook/default/index/bla/bla/bla?variable=value





的链接会产生一个对cookbook应用程序的 default.py controller中的 index() 函数的一个调用。





“bla”, “bla”, 和 “bla” 将被传递为 request.args[0:3] ,而”value”将被保存在 request.vars.variable 中。




controller函数应该象下面一样返回一个dict(字典)




return dict(name=value, othername=othervalue) 这样变量 name 和 othername 将被传到相应的view中。




现在试一试,从 cookbook/design 来创建一个 test.py controller(只需要输入名字并点击Submit),编辑 test.py 然后创建你自已的 index 函数。 [译注:如果文件名不带.py扩展名则web2py会自动添加,其它的也类似。]





回到 cookbook/design 并在 test.py 所暴露出来的 index 函数上点击。





web2py使用了 generic.html view(它是从基础的 layout.html 扩展来的)来对你的 index() 函数返回的变量进行渲染。




激动的时刻开始了...
模型创建
回到 cookbook/design ,然后创建一个叫 db.py 的新model(只需要在适当的字段上输入 db 然后点击Submit)。model定义在这里与Rails有些不同。在web2py中,一个model是一个包含了在每个数据库中 所有表 的定义文件。





编辑刚才创建的 db.py model,然后输入下面的代码:





这个model定义了两个表 category 和 recipe 。**recipe** 有一个 category 字段,它是一个对 db.category 的引用,并且 date 字段缺省为今天。每个字段有一些要求(这是可选的), categore.name 要求一个新值要满足 IS_NOT_IN_DB(字段必须唯一), recipe.category 要求这个字段满足 IS_IN_DB(引用是有效的), recipe.date 要求它要包含一个有效的日期。 [译注:如果你同时希望它可以为空,或不为空时需要为有效的日期格式,可以使用 IS_NULL_OR(IS_DATETIME())]

2


这些要求将被强制使用在任一入口的表单中,无论它是管理界面部分或用户生成的部分。 [译注:web2py会提供象SQLFORM这样的东西进行记录的录入,用户也可以使用它。而SQLFORM会对字段的约束项进行检查]




数据库管理界面(appadmin)
回到 cookbook/design ,在model下,你会看到两个新的链接,分别为 database administration 和 sql.log 。在前者上点击,如果不存在拼写错误时你会看到:





这是你的数据库管理界面。试着插入一个新的category记录:





和一些新的菜谱:





这难道不比Rails简单吗?甚至都不需要和PHP、JSP、ASP、J2EE等相比。




是谁创建的这些表呢?web2py干的!web2py会查找一个叫做db.db的数据库( [译注:在SQLDB中定义的文件名,因为使用的是SQLite3数据库。同时使用SQLite3你还可以使用绝对路径。否则它会在你的应用程序目录下的databases子目录下创建这个数据库文件。]),如果找不到,那么它会创建这个数据库和你刚才定义的表。如果你修改了一个表的定义,web2py会替你修改表结构。如果你定义了另一张表,它会被创建。你可以看一下由web2py为这种迁移所生成的SQL,通过点击 sql.log 。




随便探索管理界面,插入几条记录,并试着列出它们。





这张表可以通过点击表头进行排序,并且当记录超过100条时会进行分页。试一下JOIN(表的联接操作),在SQL FILTER字段上输入 recipe.category=category.id 。





字段 id 是从哪来的?在web2py中的每张表都有一个唯一的整数键叫做 id 。如果你在表的 id 值上点击,你就可以单独修改这条记录。




注意 appadmin.py 是你的cookbook应用程序的一部分,所以你可以对它进行阅读和修改。在这个教程中,我们不这样做,而宁愿从头编写一个新的controller。这样会起到更好的宣传作用。




创建函数(Action)
在 cookbook/design 中,编辑 test.py controller 并加入如下代码:




def recipes(): records=db().select(db.recipe.ALL,orderby=db.recipe.title) return dict(records=SQLTABLE(records)) 现在回到design,在 recipes 上点击,你会看到





注意,传给view的变量 records 是一个 SQLTABLE 对象,它知道如何把自身渲染为CSS友好的HTML。变量 records 是由 generic.html view来渲染的。




让我们再改一下。修改controller为:





注意:




recipes 现在返回记录列表,而不是一个SQLTABLE,而且它会从表的 category 字段生成一个选择 表单 .





show 接受 rquest.vars.id ,并且执行选择,一旦失败,它会重定向到 recipes 。







new_recipe 返回一个SQLFORM对象,它可以根据一个表(db.recipe)的定义创建一个HTML表单。 form.accepts() 执行对表单的校验(根据模型中的需求),用错误信息来更新表单,如果检验成功,它插入新记录到数据库中。







URL(r=request,f='function') 可以在当前的应用程序和controller中生成 “function” 的url,根据HTTP请求。







这块代码使用通用view已经可以完全运行,但是我们将在下面的布局层执行额外的客户化处理。




注意,一些检验器(validator),象用于’datetime’字段上的 IS_DATETIME() ,缺省是自动设置的。 [译注:如果你不想使用缺省的validator可以设置字段的 ``field.requires=[]`` ]




创建View
现在为 recipes 创建一个view。这个view叫做 test/recipes.html (在适当的字段上输入带路径的名字然后点击Submit)。





编辑新创建的文件





现在再试着调用 recipes





注意在 {{ }} 标签中的代码是Python代码,需要注意:




不需要缩近,一个代码块以末尾为冒号的行开始,到以开始为pass的行结束(例如:def :return, if:elif:else:pass和try:except:pass)。





view可以看到在model中定义的所有东西加上由controller返回的变量。







{{=something}} 将把 something 渲染成为HTML,之后对特殊字符进行转义。







注意




{{=A(message,_href=link)}}它是一个html辅助函数。会简单地替你输出




<a href= "link">message</a>标签。




创建 test/show.html ,包含:





看上去象这样:





最后创建一个 test/new_recipe.html ,包含:





看上去象这样:





注意web2py在表格中是如何把字段名首字母大写的,并且根据指定的需求为category字段生成了一个 SELECT/OPTION 字段。




如果你不喜欢 [web2py]cookbook 的广告条或它的CSS,你可以在 layout.html 文件中编辑它们。




一些魔术
如果你试图提交一个不满足需求(例如试图提交一个空的菜谱)的表单,web2py会通知你相关的提示信息。





结论
我们已经编写了一个可工作的web2py应用程序,仅仅通过浏览器,几个点击和总共53行代码。我们还得到了一个自由使用的数据库管理界面,它可以让你插入、选择、更新和删除单条记录或记录集。




web2py也包括了容易使用的函数来导入和导出CSV格式的表数据,生成RSS feed和RTF文件(与MS Word兼容),处理JSON用于AJAX。




如果想要了解更多关于web2py,请访问网页: http://mdp.cti.depaul.edu




如果你有问题,请加入Google用户组: http://groups.google.com/group/web2py?hl=en




附录 数据库API
连接到sqlite3数据库文件test.db




>>> db=SQLDB("sqlite://test.db") 或连接到MySQL数据库




>>> db=SQLDB("mysql://username:password@host:port/dbname") 或连接到PostgreSQL数据库




>>> db=SQLDB("postgres://username:password@host:port/dbname") 可用字段类型




>>> tmp=db.define_table('users',\ SQLField('stringf','string',length=32,required=True),\ SQLField('booleanf','boolean',default=False),\ SQLField('passwordf','password'),\ SQLField('textf','text'),\ SQLField('blobf','blob'),\ SQLField('uploadf','upload'),\ SQLField('integerf','integer'),\ SQLField('doublef','double'),\ SQLField('datef','date',default=datetime.date.today()),\ SQLField('timef','time'),\ SQLField('datetimef','datetime'),\ migrate='test_user.table') 一个字段就是一个SQLField类型的对象




>>> SQLField('fieldname', 'fieldtype', length=32,\ default=None,required=False,requires=[]) 删除表




>>> db.users.drop() 插入、选择、更新、删除的例子




>>> tmp=db.define_table('person',\ SQLField('name'), \ SQLField('birth','date'),\ migrate='test_person.table') >>> person_id=db.person.insert(name="Marco",birth='2005-06-22') >>> person_id=db.person.insert(name="Massimo",birth='1971-12-21') >>> rows=db().select(db.person.ALL) >>> for row in rows: print row.name Marco Massimo >>> me=db(db.person.id==person_id).select()[0] >>> me.name 'Massimo' >>> db(db.person.name=='Massimo').update(name='massimo') >>> db(db.person.name=='Marco').delete() # test delete 更新单个记录




>>> me.update_record(name="Max") >>> me.name 'Max' 复杂搜索条件




>>> rows=db((db.person.name=='Max')&\ (db.person.birth<'2003-01-01')).select() >>> rows=db((db.person.name=='Max')| \ (db.person.birth<'2003-01-01')).select() >>> me=db(db.person.id==person_id).select(db.person.name)[0] >>> me.name 'Max' >>> rows=db(db.person.birth.month()==12).select() >>> rows=db(db.person.birth.year()>1900).select() >>> rows=db(db.person.birth==None).select() >>> rows=db(db.person.birth!=None).select() >>> rows=db(db.person.name.upper()=='MAX').select() >>> rows=db(db.person.name.like('%ax')).select() >>> rows=db(db.person.name.upper().like('%AX')).select() >>> rows=db(~db.person.name.upper().like('%AX')).select() orderby, groupby 和 limitby 的使用




>>> people=db().select(db.person.name,orderby=db.person.name) >>> order=db.person.name|~db.person.birth >>> people=db().select(db.person.name,orderby=order) >>> people=db().select(db.person.name,orderby=order,\ groupby=db.person.name) >>> people=db().select(db.person.name,orderby=order,limitby=(0,100)) 一对多关系的例子




>>> tmp=db.define_table('dog', \ SQLField('name'), \ SQLField('birth','date'), \ SQLField('owner',db.person),\ migrate='test_dog.table') >>> dog_id=db.dog.insert(name='Snoopy',birth=None,owner=person_id) 简单JOIN(连接)




>>> rows=db(db.dog.owner==db.person.id).select() >>> for row in rows: print row.person.name,row.dog.name Max Snoopy 多对多关系的例子




>>> tmp=db.define_table('author',SQLField('name'),\ migrate='test_author.table') >>> tmp=db.define_table('paper',SQLField('title'),\ migrate='test_paper.table') >>> tmp=db.define_table('authorship',\ SQLField('author_id',db.author),\ SQLField('paper_id',db.paper),\ migrate='test_authorship.table') >>> aid=db.author.insert(name='Massimo') >>> pid=db.paper.insert(title='QCD') >>> tmp=db.authorship.insert(author_id=aid,paper_id=pid) SQLSet




>>> authored_papers=db((db.author.id==db.authorship.author_id)&\ (db.paper.id==db.authorship.paper_id)) >>> rows=authored_papers.select(db.author.name,db.paper.title) >>> for row in rows: print row.author.name, row.paper.title Massimo QCD 用belongs进行搜索




>>> set=(1,2,3) >>> rows=db(db.paper.id.belongs(set)).select(db.paper.ALL) >>> print rows[0].title QCD 嵌套选择




>>> nested_select=db()._select(db.authorship.paper_id) >>> rows=db(db.paper.id.belongs(nested_select)).select(db.paper.ALL) >>> print rows[0].title QCD 以CSV格式输出




>>> str(authored_papers.select(db.author.name,db.paper.title)) 'author.name,paper.title\r\nMassimo,
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值