利用web.py+html(bootstrap)+mysql实现了一个小型的设备管理系统,在这个过程当中遇到不少问题,将问题及解决方案总结以下,有遇到相似问题的同窗,但愿能够帮到大家。javascript
一、关于中文的编码方式,mysql+python+web.py+html的这个东西,从html页面输入中文提交insert到数据库,再从数据库中读取
内容到展现在html页面上,中文的乱码问题的处理,具体见虾米的博客中,写了这个处理方式,主要就是:
(1)各个部分的编码方式都保持惟一
(2)在mysqldb中的cursor.py的文件中的encode中的charset直接改为了'utf-8',这样就能将中文正确插入到数据库了
(3)而后在对数据库进行get操做的全部py文件的类方法中,conn时最后面增长一个charset="utf8"的参数,这样就能从数据库中
取出中文并显示正确了css
二、今天遇到了在html经过python语句for打印出来全部语句的时候,使用range(1,len(xxx))的时候,提示'int' object is not callable,可是换成了xrange就能够了,
刚开始一直认为是range和xrange之间有什么区别,致使出现这个问题,后来一点一点排除,发现是range的问题,再往上查看代码,发现先定义了一个range的变量(是int型的),因此后面再用python的内置方法range()时出现提示"'int' object is not callable"的提示;html
range和xrange的区别:(在python 2.X下)java
range的返回结果是一个list、xrange的返回结果是一个迭代器,二者均可以用于for循环,可是range由于会生成一整个list,因此占用空间要比xrange大,速度上也会比xrange要慢。python
通常状况下循环都用range,可是若是是极大长度的数组就须要用xrange,生成器的意思是不预先分配数组长度的空间 是循环到哪 分配到哪mysql
另外,针对这种遍历的操做,若是须要取元素内容的,直接用for one in llandroid
若是同时须要迭代变量和元素内容的,直接用for i, item in enumerate(ll),其中ll能够是list、dict、tupleios
三、url的控制相关的须要总结(尤为是xx\(d+)这样子的跳转控制)web
url跳转格式:正则表达式
urls = (
'/', 'index',
'/login', 'login')
urls是一个全局变量,在web.py中可以被识别为全部url跳转的存储信息站,以元组的形式存储,前面是传进来的url的正则表达式,后面是处理类,如第一行的"/",就是说:http://localhost:8080/在浏览器中点击enter以后,程序就会收到这个请求是"/",而后后面的处理类是index,就会去找class index,在index中根据状况,肯定下一步的跳转逻辑,须要请求数据返回结果的,经过def GET(self)方法来实现,若是要提交结果并根据提交处理的结果返回必定的内容,就要走POST请求,经过def POST(self)方法来实现
还有一种跳转格式是这样的:'/updateinfo/(\d+)', 'updateInfo',
那么这种主要是用于什么状况呢?在updateInfo类中又如何体现这个"(\d+)"呢?
答:主要用于以下状况,好比你的程序有一个表格显示了多行数据,每一行数据对应一个特定的ID,固然你能够拿到这个ID,而后你能够经过点击一个修改连接,如"修改"的连接来跳转到另外一个页面,可是在跳转到的另外一个页面上,须要先显示出来以前的表格内容,这种状况就须要这样处理了,固然若是在一个内部系统内,没有什么安全问题的话,直接写ID就能够,要是有安全问题的话,可能就须要作一个md5加密之类的了。
接下来讲这个'/updateinfo/(\d+)',第二个"/"的"(\d+)",其实就是一个正则表达式,这样的话,根据你须要传递的信息内容,定义正则表达式的格式。
在具体的updateInfo类中的def GET(self)和def POST(self)方法,又是如何用到这个"(\d+)"的呢?
答:方法须要修改为def GET(self, info_id)和def POST(self, info_id)了,而后在程序中直接写info_id = int(info_id)就能够获得你想要的值了,接下来就能够用这个值操做数据库的值了,好比获取当前ID对应的数据信息,好比修改当前ID对应的数据信息
四、page分页相关的须要总结
其实分页主要分为如下几个部分的处理:
(1)sql要针对一页显示的数目设置sql语句,会包含offset设置偏移量用来获得正确的每页应该显示的数据
(2)html页面上对于分页的显示相关(包括当前页数显示,总页数显示,而且可以点击某页的连接刷新出对应的数据)
处理方式以下:
(1)对于sql的处理——
在主业务逻辑中须要从web.input()中获得当前须要显示的page数,在def GET(self)具体代码以下:
i = web.input(page='1') page = int(i.page)
注意,这里web.input(page='1')在括号中的page='1'至关于设置一个默认值,若是当前没有传入page值的话,就表明是请求第一页的数据,若是传入的是非第一页的值,则获得正确的page参数值,以后再用这个page变量做为参数,传给model.py中的进行数据库操做的类的某个方法
在具体的数据库操做的类的方法中,获得page参数,而后具体代码以下:
per_page = settings.per_page
# 获取本次须要检索的offset偏移量的值 offset = (page - 1) * per_page if userid != '': sql = "select * from androiddevice where storeman='%d' order by androiddevice.id desc limit %d offset %d" %(userid, per_page, offset) else: sql = "select * from androiddevice order by androiddevice.id desc limit %d offset %d" %(per_page, offset) print 'sql', sql
其中per_page是从settings.py文件中获取到的每页显示x个的数据值,而后根据这个值获得offset偏移量的值,注意limit后面是%d,offset后面也是%d,不用再加''了,不然就会不被当作一个int型的值
(2)html页面的处理——
$code: grace = 5 range1 = grace * 2 start = page - grace if page - grace > 0 else 1 end = start + range1 if end > page_count: end = page_count start = end - range1 if end - range1 > 0 else 1
在$def with()的下方定义$code,而后获得start和end变量,以后在html的页面适当位置显示
<div id="post_pager"> $if start > 1: <a class="page" href="/list2personiosdevice?page=1">1</a> ... $for i in xrange(start, end+1): $if i == page: <span class="page">$i</span> $else: <a class="page" href="/list2personiosdevice?page=$i">$i</a> $if end < page_count: ... <a class="page" href="/list2personiosdevice?page=$page_count">$page_count</a> </div>
而后就能正确显示1,2,...x页的这种形式,而且均可以点击跳转进而显示正确的page页面上对应的数据显示
五、sql的内容(还有就是几种不一样的python里面sql的执行方式,这个须要特别注意,以及获得的值究竟是什么?是元组、仍是list??仍是其余什么??)
(1)须要安装的python库?
答:须要安装mysqldb,直接搜索mysqldb python,下载下来以后,解压获得setup.py,直接cmd里面python setup.py install,就会自动安装到site-packages下
(2)经过mysqldb进行数据库的链接?
db = MySQLdb.connect(host="127.0.0.1", user=settings.MYSQL_USERNAME, passwd=settings.MYSQL_PASSWORD, db="device_manage", port=settings.MYSQL_PORT, charset="utf8")
参数分别是host、user、passwd、db、port、charset,charset="utf8"就是为了保证从mysql的数据库中取出来的中文字符显示正确
注意host能够写成host="localhost",也能够写成host="127.0.0.1",通常状况下没有影响,可是在把代码布到另外一台服务器上时,出现了错误提示:
ERROR 2013 (HY000): Lost connection to MySQL server at'waiting for initial communication packet', system error:,网上搜了一下不少办法,好比说修改mysql的原始配置文件等,但都不行,后来直接将host="localhost"改为了"127.0.0.1"就能够了
(3)查询、插入、修改相关如何处理?
在(2)中创建了数据库链接以后,获得db对象,后续用这个db对象进行操做:
cursor = db.cursor() sql = "select * from iosdevice where id='%d'" %deviceid rslist = [] try: cursor.execute(sql) rs = cursor.fetchall() print 'rs:', rs for r in rs: print 'r.devicetype:', r[1] rslist.append({'devicetype':r[1], 'devicemodel':r[2], 'deviceopersystem':r[3], 'devicescreen':r[4], 'deviceresoratio':r[5], 'IMEInum':r[6], 'registrationID':r[7], 'companyID':r[8], 'isbreakwall':r[9], 'udid':r[10], 'remark':r[12]}) except: print 'Error, unable to fetch the data!' db.close()
以上是查询操做,sql="xxx"是须要的sql语句,%后面表示你须要替换的具体参数,经过cursor.execute(sql)执行以后,直接fetchall就能够获得查询结果,获得结果是一个list,若是不知道获得的这个变量具体的类型是什么,就可使用print type(rs)打印出来便可
插入操做的话,基本以下:
cursor = db.cursor() result = False sql = "insert into androiddevice(devicetype, brand, model, configure, \ opersystem, screen, resoratio, registrationID, companyID, storeman, remark)\ values('%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s')"\ %(device[0], device[1], device[2], device[3], device[4], device[5], \ device[6], device[7], device[8], userid, device[9]) print 'device', device print 'sql', sql try: cursor.execute(sql) db.commit() result = True except: db.rollback() db.close()
修改操做的话,以下:
cursor = db.cursor() result = False sql = "update iosdevice set devicetype='%s', model='%s', opersystem='%s', screen='%s', resoratio='%s',\ IMEInum='%s', registrationID='%s', companyID='%s', isbreakwall='%s', udid='%s', remark='%s' where id='%d'" \ %(deviceinfo[0], deviceinfo[1], deviceinfo[2], deviceinfo[3], deviceinfo[4], deviceinfo[5], deviceinfo[6],\ deviceinfo[7], deviceinfo[8], deviceinfo[9], deviceinfo[10], deviceid) print 'sql', sql try: cursor.execute(sql) db.commit() result = True except: db.rollback() db.close()
修改操做和插入操做,除了sql语句不一样以外,具体的执行都是同样的。
其实这里应该作一个sql的重构,不能来一个请求就写一个def出来,而且有不少代码是重复的,只要替换sql语句便可。
六、html相关的东西
(1)模板相关?当js与模板html中的$出现冲突时,如何处理?
首先,程序怎么找到这个html页面的呢?在程序的目录下建一个新文件夹templates,而后在主程序中写明:render = web.template.render("templates"),而后获得这个render,在具体的class的GET和POST操做的最后,就能够调用render.xxx(),其中xxx就是这个html的名字,后面能够传入参数,参数就是在html中经过python语句定义的参数;
html具体的页面如何编写呢?
好比说,不须要参数的就按照正常的html页面写就能够;若是须要参数的,须要在最上方增长:$def with(iosdevicelist),而后下面须要用到iosdevicelist的地方或者须要使用python语句的地方都须要使用$来代表这个是一个python语句或者是一个python变量。
可是js也用到了这个$来表示变量,那就把js的全部内容所有改为jQuery就好啦。
(2)bootstrap的基本内容,在html中如何用?
bootstrap其实就是为你搭建了一个很好用很丰富的css+js的库,你不用本身编写,直接引入bootstrap的源文件,而后在下面的div中经过class来使用具体的类就能获得一个好的布局+UI的效果,具体以下:
<head> <title>xx系统</title> <link href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css" rel="stylesheet"> </head>
下方在div中就能够用
<div class="container-fluid"> <nav class="navbar navbar-default" role="navigation">
其中role的含义是:让浏览器知道这是一个navigation;在div中经过class就能够获得你想要的效果
七、raise web.seeother()与return render.xxx()有什么不一样?render.xxx是怎样一个流程?
class logout(): def GET(self): session.kill() raise web.seeother('/login') #return render.login() ####### #这里必需要用raise web.seeother('/login')才能够,直接用程序跑下来的结果就是: #若是是seeother的话,url会发生变化,变成http://localhost:8080/login #若是是render.login()的话,url是:http://localhost:8080/logout #######
经过代码实践以后,发现变化就是如上注释中所说的,好比说使用render.login()退出登陆以后,再点击登陆的话,由于我在logout类中没有写POST操做,因此再次点击登陆,就会提示None,由于class logout根本没有POST方法,可是若是是raise web.seeother('/login')的话,就不会出现这个问题,由于url已经变成http://localhost:8080/login,而后点击登陆的话,是POST操做,只要去找class login类的POST操做便可
八、session的东西
(1)session与cookie有什么不一样?什么状况下用session?什么状况下用cookie?
答:讲述session与cookie区别的文章这两篇不错的:http://my.oschina.net/xianggao/blog/395675;http://www.cnblogs.com/shiyangxt/archive/2008/10/07/1305506.html
cookie——
本质:就是一小段的文本信息,通常状况下会进行加密处理,在浏览器下输入baidu.com跳转到百度搜索首页,以后在地址栏输入:javascript:alert (document. cookie)(须要有网才能看到)就能看到cookie内容
工做原理:http是无状态的协议,因此经过http请求服务器的时候,从网络链接上不知道客户身份服务器端给客户端颁发通行证,每人一个,以后访问都须要携带本身的通行证,这样就能够知道客户身份了,这就是cookie的工做原理
生存时间:若是cookie的生存时间是在本次会话周期内,浏览器会将cookie保存在内存中,浏览器关闭则自动清除cookie;不然就存储到客户端的硬盘中,浏览器关闭也不清空cookie,下次打开浏览器访问对应网站时,这个cookie就会自动再次发送到服务器端
设置步骤:客户端请求服务器,服务器会在response中颁发给客户端一个cookie,客户端浏览器将这个cookie保存起来,当浏览器再次请求该网站时,浏览器把请求的网址+cookie一块儿提交给服务器
备注:无状态协议/有状态协议?
概念在百度百科中有解释:http://baike.baidu.com/link?url=hEOZF12_v0G4fD3DxoEEd5v5bLgEEs_yai88Ic3MrZRxxA3p62xDaPHRmhGl7YGpx06q7w3HqFarqwygzUHpNq
seesion——
本质:session是服务端使用的一种记录客户端状态的机制,也会对服务器端相应的增长压力
工做原理:session保存在服务器上,当客户端浏览器访问服务器的时候,服务器就会把客户端的信息以某种形式存储在服务器上;cookie比较像是给客户端颁发了通行证,客户端经过通行证来访问服务器,session比较像是在服务器端维持了一个客户明细信息列表
通常登陆相关信息能够经过session来实现
生存时间及有效期:每次客户端有请求,就会更新session的最后访问时间并维护该session,服务器会认为该session活跃了一次;可是由于是存储在服务器端的内存中,因此为了防止内存溢出,时间较长以后,服务器端会把长时间没有活跃的session从内存中删掉,这个时间就是session的超时时间。超过超时没有访问过服务器,则session就自动失效被删除了。
设置步骤:在web.py的主程序中,增长这两行代码就能够:
store = web.session.DiskStore('sessions') session = web.session.Session(app, store)
而后就获得这个session对象了,以后在login的POST方法中,直接将须要的信息存储到session中,好比说:
session.user = username session.userid = userid session.login = True
以后判断登陆状态,以及获取跟用户信息相关的全部内容,以及判断固然登陆用户等,均可以直接从session中获取了,很是方便
具体的session相关的cookbook的内容以下:http://webpy.org/cookbook/sessions.zh-cn,在cmd下进入python环境:
输入import web
以后输入web.config.session_parameters,就能获得一个字典形式的内容,即key-value
>>> import web >>> web.config.session_parameters <Storage {'ignore_expiry': True, 'secure': False, 'cookie_domain': None, 'cookie _name': 'webpy_session_id', 'expired_message': 'Session expired', 'timeout': 864 00, 'ignore_change_ip': True, 'secret_key': 'fLjUfxqXtfNoIldA0A0J', 'httponly': True}>
具体设置值时能够经过下方内容:
web.config.session_parameters['cookie_name'] = 'webpy_session_id' web.config.session_parameters['cookie_domain'] = None web.config.session_parameters['timeout'] = 86400, #24 * 60 * 60, # 24 hours in seconds web.config.session_parameters['ignore_expiry'] = True web.config.session_parameters['ignore_change_ip'] = True web.config.session_parameters['secret_key'] = 'fLjUfxqXtfNoIldA0A0J' web.config.session_parameters['expired_message'] = 'Session expired'
如何设置session的有效期呢?
设置ignore_expiry为False,并设置上你想要的timeout便可;而后只要在timeout的时间内session一直没有活跃过,就会认为session过时,直接被服务器端删除
(2)cookie的信息都是放在request和response的http Header和http Body的,可是http消息结构是什么样的呢?
具体见文章,自行脑补,http://www.cnblogs.com/hyddd/archive/2009/04/19/1438971.html
九、这里要把整个一套流程所有理清楚,具体走的流程是什么?一步一步是怎么走的?
web.py的整个流程?从经过在浏览器中输入http://localhost:8080点击enter以后的整个流程是怎样的?
答:
须要的多个端主要包括:
一、mysql数据库
二、python文件——主程序、model(实现对数据库的操做)、其余公共文件
三、template模板文件——html文件都放在一个templates的文件夹内,而后将文件夹的路径在主程序中声明,主程序就能找到对应的html文件
四、静态文件访问——在主程序的同目录下建一个新文件夹static,这个文件夹与templates文件夹平级,而后在static目录下建img文件夹放图片资源,js文件夹放js脚本,以及css文件夹,以及fonts文件夹等;具体的引入资源的路径以下所示:./static/img表示从当前html页面返回到上一级目录而后进入到static目录以后进入img目录找到正确的图片便可
img src="./static/img/why.jpg"
五、如何跳转——
首先须要针对/操做作一个index的处理;以后跳转到对应的类,以后针对具体的每个控件其实都对应一个href的操做,这个直接写主程序中的urls的前面一项的内容,而后程序就能经过这个urls的前面一项找到其对应的后面一项,找到以后就会去对应的class查找肯定走GET处理仍是POST处理,而后再GET处理或者POST处理的最后通常会走一个render.xxx来跳转到某个特定的html或者是经过web.seeother()等提走到某个特定的html
十、数据库导入导出的方法
数据库导出——
经过cmd命令,打开安装mysql的bin目录,要用到bin目录下的mysqldump.exe的程序,注:若是你的mysql是安装在C盘下的,直接备份到bin目录下,可能会提示权限不足的问题,只要选择备份到其余目录下便可。
具体命令及步骤以下:(好比备份的数据库名称为device.sql)
cmd命令打开,进入到bin目录下,输入命令行:mysqldump -u root -p xxx>e:\xxx.sql
点击enter以后,会提示输入密码,输入正确的密码,去E盘下查看,就能生成你明明的这个sql文件了;
其中这个命令行的各个部分的含义:
mysqldump表示用的是bin目录下的mysqldump.exe的这个程序,而后参数分别为-u root(用户名) -p 后面加的不是密码,xxx表示你想要备份的数据库,后面的e:\xxx.sql你本身来写,建立到你想要的路径下便可,注意后缀名
数据库导入——
打开数据库的命令行窗口,输入密码,以后show databases; 而后再use xxx(老的数据库),以后输入source device.sql(固然你以前的数据库必须得是这个名字,不然找不到就不会覆盖恢复的)
十一、部署到一台新机器上时,出现了提示"ERROR 2013 (HY000): Lost connection to MySQL server at'waiting for initial communication packet', system error:",解决方案,就是把host的localhost改为了127.0.0.1,这个也是从网上查到的
答:多是由于localhost跟127.0.0.1之间的关系创建有问题,因此讲locahost改为127.0.0.1以后,可以找到本地的数据库了,就正常了;
十二、excel数据直接导入到sql中——这个要记录一下
问题——以前组内的设备都是直接记录在excel中的,须要从excel中直接导入到sql中
处理方法——最开始想了不少方法,好比把excel的数据先所有转成json或者xml,而后再本身写程序将json或者xml转成sql的insert语句,而后批量插入;后来很偶然的在excel的数据的菜单栏下看到了Mysql for Excel Database的一项,固然是由于本地安装了数据库,而后后续就直接调整excel的格式,而后将excel的数据直接append到table中便可,确实省了不少事情呀,工具真的很须要也确实强大。