最近用应开辟的过程中现出了一个小题问,趁便记载一下原因和方法--模板数据
用应python境环中的tornado行进web开辟上篇已决解了urlmap和基本行运机制的题问。接下来行进web程编就是一下几个题问
- 1.输入数据的取获和输出运算结果
- 2.数据库操纵
- 3.自带模板的用应
当然还有cookies和session等,这些杂项,不在本篇行进学习。
1.输入数据的取获和输入运算结果
WEB作为一种UI达表方法,最主要的是取获数据和达表运算结果,所以如在何一个web开辟框架中取获url或post递传过去的数参十分主要,我议建学习部全的Web开辟语言或框架都从此开始比拟好。在tornado中从我自己的懂得来看有两种取获数参的方法
-
a.通过urlmap做正则达表式,通过取抓组来直接取获数参
这就要需我们在做URL置配时就想好要递传的数参如,我们要需给ArticleDetail类递传一个文章编号,我们晓得这个编号只能是字数类型的。那么可以在置配时写成如下模样
handlers =[(r'/detail/(\d+)',ArticleDetail),]
其中的(d+)就是一个正则的取抓组,用于取获多位的字数。 那么在ArticleDetail类的get或post数函实现时就要写成
classArticleDetail(BlogHandler):defget(self, id):....详细的实现
进口时多了一个id数参,当然你也可以用别的变量名,都会将URL匹配的结果放进这个数参传入的。
-
b.通过在get或post中用应tornado的RequestHandler的get_argument数函来取获数参
为了样同取获到文章id这个数参,如果我们不在URL置配时做正则(一般我都不做,因为我对正则不很纯熟),也可以在详细处置某个址地的类方法中用应get_argument来得获数参值,还是以取获文章编号为例,我们要需如下方法来编写Url置配
handlers =[(r'/detail',ArticleDetail),]
在址地义定中我们取消了正则匹配组,让址地直接交给ArticleDetail类全权担任。 接下来在ArticleDetail中get或post数函的进口就不能在有除self不测的数参了,当用应/detail?id=1问访系统时
classArticleDetail(BlogHandler):defget(self): id=self.get_argument('id')#取获到id....详细的实现def post(self): id=self.get_argument('id')#取获到id....详细的实现
对于输出,一般在任何WEB开辟框架中都比拟单简,tornado中就是单简的self.write(htmlsrc),其中htmlsrc就是即将交给浏览器示显的html文件的码源。一般是由模板引擎将数据与模板结合后字符串结果。
2.数据操纵
WEB开辟与其他开辟分歧,也要需及涉数据的久持化和数据的读取。WEB的UI是HTML决解件软的界面,数参的集收,事件的触发,结果的示显;开辟语言担任决解运算逻辑,数据读取和存保;数据库和数据文件或其他方法的久持化决解象对久持化和数据源题问(当然还有一种叫oracle的数据库!!!!它太NB了,自己都能写WEB,也能写程序,与我懂得上数据库应当干的活别区大较,请观众意随吐槽);所以决解数参取获和数据库操纵,基本上就决解了WEB开辟的大最题问了(jquery?html?这也是很大的题问,但属于前端开辟的主要学习方向)。
tornado内置了一个单简封装了Mysql的操纵,pypm install python-mysql要需事前安装好动驱哦。公布出来的数函比拟少,与直接用应python的数据接口基本分歧,稍稍单简写。
-
a.数据连接
#导入tornado的数据库包from tornado import database #建创数据库连接,db_host数据库主机名,db_port开放的端口号,db_user户用名,db_passwd登录密码,idle_time大最闲空时光认默是7小时以秒为单位,mysql has gone away一般跟这个有关系。种这连接方法不支持连接池。较原始,效率还可以。 db = database.Connection('%s:%s'%(db_host, db_port), db_name, db_user, db_passwd, idle_time)
建创数据库连接后就能够用应db来行进后边分析的基本操纵了。
-
b.查询 用常的查询方法如下
1). query(),用于执行select 语句,返回的是行集list,例如
users=db.query("select * from users") for u in users: print u.username,u.userpwd user=db.query("select * from users where id=%s",id) if user: print user[0].username,user[0].userpwd
2).get(),返回的是符合条件的结果集的第一行
user=db.get("select * from users where id=%s",id)if user:print user.username,user.userpwd
3).execute(),用于执行select外以的语句,返回值基本视无
rv=db.execute("update customer set sex='男' where id=10128") print rv
-
基本上如果你的语句没写错,到得的都是0
-
c.数据结果
晓得如何查询后续就是如何用利查询到得的结果了。
对于query()操纵返回的是[]组数(命名位items),
组数中每个元素均是{}字典(每个都是item)。
如果确实晓得字段名(性属名)如确实晓得查询结果中至少有一行数据,含包名为title的字段,可以直接用应items[0].title到得数据。
如果不确实晓得字段名,或字段值可能为None,可用应items[0].get('title','')来取获值。 最用常的方法是循环
for item in items:print item.get('title','')
-
d.其他操纵
-
execute_rowcount(sql)用于取获查询或新更到得的记载行集的行数,特殊有意思的是如果你要新更的容内原来已是那个模样,如
print db.execute_rowcount("update customer set sex='女' where id=10128")print db.execute_rowcount("update customer set sex='女' where id=10128")
执行两遍到得的结果是1,0。我来原始终认为数据库很弱智的,看来是我那时候不懂事,很真天。
- close()用于手动关闭数据连接,在小型用应中我们基本不会看到他的现出
-
3.自带的模板的用应
web程编其实可以用不模板的,但是那样不仅写的时候苦痛,写出来后以更苦痛
-
写的时候,html代码与python代码混合,连接字符串非常费劲
-
改的时候很易容现出误错,python又不是译编执行的,上线了执行到某误错时,多恼火,级低愚昧的误错易容犯
-
换web的UI时种这写法根本没办法操纵,那就是写重一次程序啊,杀人的心都有的
所以如果你开辟了一个Web系统,但是没有用模板术技,这个系统就只能给钱多人傻的单位做了。
mako是我最欢喜的模板,但是学习tornado么,而且不是那种排版敏感的,先来学习用用它自带的。
web用应开辟用应模板引擎我么要主要决解一下几个主要的题问,在tornado中我们一个个解破
- 1.径路和模板放存方法
善用os.path.dirname(file)来决解径路题问,例如我们的模板预备放在程工的根目录下的/T/目录中,对于模板放存的目录我们可以这样义定
loader =template.Loader(os.path.dirname(__file__)+"/T/")
当然我更偏向直接义定绝对径路的方法来决解这个题问,因为用应上也特殊单简,而且可以把模板放在与源代码有关的目录中,上线用应时也更加安全
TP="D:\\T\\" loader=template.Loader(TP)
模板一般就是一般的文本文件,便利起见一般以html扩展名放存比拟好。比如index.html,这样我们把index.html放入我们义定的模板加载径路后用应以下代码加载模板引擎
t=loader.load("index.html")
其中t就是我们直接可以用应的模板了。
- 2.数据与模板如何结合
模板主要功能之一就是决解数据的示显方法,如何把我们的数据交给模板用应?首先我们来取获数据
cus1=db.query("select * from customers limit 3")#到得数据 cus2=db.query("SELECT * FROM customer LIMIT 5 OFFSET 5")#到得数据 htmlsrc=t.generate(datas1=cus1,datas2=cus2)#模板运算出的字符串作为html示显self.write(htmlsrc)#向客户端发送结果
代码第三行的datas1和data2是在tornado模板文件中将要引用的变量名我们的模板编写如下
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><body><!--这里从datas1中读取数据--><tablestyle="border:1px solid"> {% for u in datas1 %} <tr><td>{{ u.truename }}</td><td>{{ u.sex }}</td></tr> {% end %} </table><hr/><tablestyle="border:1px solid"> {% for u in datas2 %}<tr><td>{{ u.truename }}</td><td>{{ u.sex }}</td></tr> {% end %} </table></body></html>
可以兴奋地讲:
1.tornado的模板不要需严厉按照python的缩进来写书
2.tornado的模板用应{{变量或象对}}来取获详细的变量值或象对性属值
3.tornado的模板中用应{% for var in vars %}....{% end%}来实现循环
4.tornado的模板中用应{% if 条件 %}...{% elif 条件 %}...{% else %}...{% end %}方法实现模板内的判断
5.tornado的模板用应{% include 文件名 %}来含包子模板,同时也会运算子模板的运算逻辑
OK晓得以上这些容内或许我们已可以开始一个稍稍庞杂一点儿程序了。面下我们来系联一下。
4.综合示例
制造的目标做一个对一个数据表行进增长、删除、改修和单简查询的程序
数据表我们定假为通讯录数据
为了简化程序的行运逻辑,尽可能用不
ajax
或其他要需一些
javascript
基本的写法
数据库当然用应
mysql
啦
我按照一般惯习的开辟过程来成完这个demo,综合以上的知识,我尽量少写代码外以的字文了,写尽可能过细的注释。功能虽然特殊单简,都是按照时平现实项目的开辟标准和架构来执行的。
a.数据结构及问访限权
我们用应root户用来问访数据库,定假问访的密码是root123456,用应以下语句来建创数据表:
CreateTable CREATE TABLE `d_contacts`(`id`int(11) NOT NULL AUTO_INCREMENT,`truename` varchar(20) DEFAULT NULL,`sex` varchar(2) DEFAULT NULL,`phoneno` varchar(20) DEFAULT NULL,`email` varchar(20) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8
b.划规目录结构
我们定假开辟的项目文件放存在D:\projecting目录中,项目名称叫GingerTornadoDemo1
c.关键性码源解释
- 1).处置类的基类 basehandler.py
代码如下,通过义定基本类,可以让后续的开辟变得更加单简,这里用应了一个巧技性的变更。 用利了Aaron Swartz的web.py框架中的Storage类来简化数参取获。前以要需一个个用get_argument()来取获的数参当初只要用i=self.input() 然后直接取获数参名如i.id就能够到得了。当然数参如果不存在仍然会报错的。取获后最好用utils包中的InitStorage做一次初始化(不会污染已有性属值),确保调用无误。
1 # -*- coding: utf-8 -*- 2 import json 3 __author__ = '[email protected]' 4 from tornado.web import RequestHandler 5 from Storage import storage 6 class basehandler(RequestHandler): 7 """ 部全Handler基类 """ 8 def input(self): 9 """取获到部全的输入数据,将其转换成storage便利调用""" 10 i= storage()#初始化一个容器 11 #到得部全的输入数参和数参值 12 args=self.request.arguments 13 #将数参写入i的性属 14 for a in args: 15 i[a]=self.get_argument(a) 16 #取获file类型的数参 17 i["files"]=storage(self.request.files) 18 #取获path 19 i["path"]=self.request.path 20 #取获headers 21 i["headers"]=storage(self.request.headers) 22 return i
- 2).数据操纵类 d_contacts.py
此文件存保于models目录中,用来做dcontacts数据表的基本操纵,我单简封装了一些基本的数函。有些可能未在本demo中用应过,但我认为很主要也编写了出来。 在python中JAVA和C#中的失血模型由一个单简的Storage基本部全搞定,而且可以很便利的行进数据结构的调整和性属类型变更和补充性属。所以这个类也可以看出来Java和C#那种长时光对我码编风格的损害有多深了。部全的程序都可以放一个文件中决解,但我仍然要把他们的功能按照多层的思想来细分。 这个类可以视作DAL类,如果有更庞杂的业务逻辑操纵还可以继承或调用dcontacts来编写更多的业务逻辑(至于是继承好还是调用好,我一般选择继承,虽然有违与关闭开辟的面向象对接线的传统,但便利啊。谁用谁晓得。)。
聪明人学习,像搏击长空的雄鹰,仰视一望无际的大地;愚笨的人学习,漫无目的,犹如乱飞乱撞的无头飞蛾;刻苦的人学习,像弯弯的河流,虽有曲折,但终会流入大海;懒惰的人学习,像水中的木头,阻力越大倒退得越快。
# -*- coding: utf-8 -*- __author__ = '[email protected]' from config import sdb from Storage import storage def GC_set(obj): return "" class d_contacts: fields=["id","truename","sex","phoneno","email"] #义定好数据表有的数据 def getAll(self): """取获部全记载合集""" items=sdb.query("select * from d_contacts") if items: for i in items: i=storage(i) return items def delEntityById(self,id): """ 根据id来删除数据 """ return sdb.execute("delete from d_contacts where id=%s",id) def getEntityById(self,id): """ 根据id来或取象对实例""" if id: id=int(id) item=sdb.get("select * from d_contacts where id=%s",id) return storage(item) else: return None def getRowsBySqlwhere(self,sqlwhere): """ 根据sqlwhere来查询数据合集""" items=sdb.query("select * from d_contacts where id>0 "+sqlwhere) if items: for i in items: i=storage(i) return items def update(self,obj): """ 新更数据行集 """ obj=storage(obj) return sdb.execute("update d_contacts set truename=%s,sex=%s,phoneno=%s,email=%s where id=%s",obj.truename,obj.sex,obj.phoneno,obj.email,obj.id) def insert(self,obj): """ 写入数据 """ obj=storage(obj) return sdb.execute("insert into d_contacts (truename,sex,phoneno,email) values(%s,%s,%s,%s)",obj.truename,obj.sex,obj.phoneno,obj.email) def getRowBySqlwhere(self,sqlwhere): """ 根据sqlwhere来取获一个数据实例 """ item=sdb.get("select * from d_contacts where id>0 "+sqlwhere) if item: item=storage(item) return item
- 3).详细业务控制实现类 f1001.py
这是部全操纵数据表dcontacts的详细业务逻辑的实现。此文件放存于f10目录中,同过它调用models包的dcontacts等来实现对d_contacts数据的操纵和觉视达表。 既然是demo我就趁便用应了一下单简的ajax操纵,来成完增、删、改的功能。这样就基本涵盖了大部分的web开辟的基本性功能的实现方法了。
# -*- coding: utf-8 -*- __author__ = '[email protected]' import os from tornado import template from Storage import storage #导入工具数函 from utils import * #导入基本类 from basehandler import basehandler #出导久持类 from models.d_contacts import d_contacts #导入模板 from config import TP #建创久持象对 optobj=d_contacts() #建立模板,指定模板径路 tl=template.Loader(os.path.join(TP,"f10/T")) class List(basehandler): """ 用于处置示显列表 """ def get(self): #取获部全输入数参 i=self.input() #取获部全数据,续继改下去一定要虑考分页 i.recs=optobj.getAll() #调用模板 t=tl.load("f1001_list.html") #把数据推给模板行进运算 htmlsrc=t.generate(i=i) #示显运算结果 self.write(htmlsrc) def post(self): """由于不要需处置POST,所以不实现 """ pass class Update(basehandler): """ 用于处置示显改修界面,处置ajax改修、新增、删除的请求 """ def get(self): i=self.input() #初始化i的rec性属,让Rec有具与数据表字段名的部全性属 i.rec=InitStorage(i,["id","truename","phoneno","email"]) #从数据库中取获数据 i.rec=optobj.getEntityById(i.id) #调用模板 t=tl.load("f1001_update.html") #把数据推给模板行进运算 htmlsrc=t.generate(i=i) #示显运算结果 self.write(htmlsrc) def post(self): """ 处置写入数据删除数据和存保数据等操纵 """ def valid(v): """ 验证邮件否是准确 """ if IsEmail(v.email): return True else: return False #特殊要注意,因为是响应的ajax的请求,返回数据类型需指定为json self.set_header("Content-Type", "application/json") #做一个容器变量 v=storage() #取获到部全的输入值 i=self.input() #将输入数参中与数据表字段名分歧的值值赋给v象对的性属 v=CopyData_INC(v,i,optobj.fields) #根据操纵类型来行进处置 if i.act=="add": vv=valid(i) if vv==True: optobj.insert(v) #返回处置结果JSON值 self.write(JsonResult("OK")) else: self.write(JsonResult("NOOK")) if i.act=="update": vv=valid(i) if vv==True: optobj.update(v) self.write(JsonResult("OK")) else: self.write(JsonResult("NOOK")) if i.act=="del": optobj.delEntityById(i.id) self.write(JsonResult("OK"))
- 4).其他
开辟必定要需用到html和javascript,是难点但不是我们学习tornado的重点。详细看代码吧。 我只解释一下f1001_update.html模板中一段javascript
var rv={}; $("[rel='v']").each(function(){ rv[$(this).attr("id")]=$(this).val(); }); ....body中有如下元素.... <td><input type="text" rel="v" name="truename" id="truename" value=""/>*</td> <td><input type="text" rel="v" name="sex" id="sex" value=""/>*</td> <td><input type="text" rel="v" name="phoneno" id="phoneno" value=""/>*</td> <td><input type="text" rel="v" name="email" id="email" value=""/>*</td> ....
这是我几乎在部全项目的录入模板中都用应的一个小巧技,我额外埠给部全要需取获值的控件增长了一个rel性属。 用利jquery的选择性特来遍历id后动自取获这些组件的值,再将值值赋给实现义定的字典rv。 这样做的利益是,你几乎从前台到后台都不要需在数参值的取获上再一个个地行进值赋了。rv象对可直接在ajax调用时用应,无需处置。ajax调用数参传给处置的类后,用应i=self.input(),又将部全的数参和值变成了i的性属和值。 接下来你要做的就是用应i这个已值赋好的象对来做你的预算了,部全以往取获数参和递传数参的烦麻就全决解了。
单简的描述你定肯受感不到这样做的利益,但当你对面一个有具几十个数参要需录入,后台对应一个数据象对时,你就会深深地爱上我的种这写法。级超爽,用不写一个值赋和取获数参的代码啦。还是去休会码源吧。
-
5).果效和码源下载
列表功能:
改修功能:
最好的档文是码源:tornado综合示例
5.论结与其他
- 原来想写3~5篇的帖子,发明两篇就搞定了
- 定肯是疏漏了很多西东啊,权鉴、404、UI组件、分页等等,还有很多。但我认为那些不是骨干。骨干是我分析的这些,有这些你就能够用应tornado来工作了
- 我学习到这里受感tornado挺不错的,但觉感在开辟的效率上来看,与web.py还存在相当大的差距。我自己的法想是用应tornado来做web服务器,做urlmap。好了,其他的数据库组件还是续继用应web.py的,模板续继用应mako比拟和我的胃口。因为我开始学习tornado仅是因为它的性能不错,名望不小。而且在Aaron Swartz杀自后,web.py后续的展发给我自己在组件的选择上撬开了一个小小的缺口。别无它。
- 欢喜的兄弟拿来学习用用,不欢喜也可以来吐槽。我觉感我基本说清楚了tornado的Web开辟。语言限有,我对他的系统学习到此为止了,再有题问,个别看它的档文相信是小菜了。
【原创载转】http://www.youliaoo.com/post/20
文章结束给大家分享下程序员的一些笑话语录: 苹果与谷歌之争就是封闭收费与自由免费思想之争。(别急着把google来膜拜哦?那可是一家公司,以赚钱为目标的公司!当年我Party就是这样把广大劳动人民吸引过来的。今天的结果你们都看到了。)