web.py+html+mysql实现web端小系统的问题汇总

利用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

协议的状态是指下一次传输能够“记住”此次传输信息的能力.
http是不会为了下一次链接而维护此次链接所传输的信息,为了保证服务器内存.
好比客户得到一张网页以后关闭浏览器,而后再一次启动浏览器,再登录该网站,可是服务器并不知道客户关闭了一次浏览器。
因此是无状态协议
而DNS是有状态协议
因为Web服务器要面对不少浏览器的并发访问,为了提升Web服务器对并发访问的处理能力,在设计HTTP协议时规定Web服务器发送HTTP应答报文和文档时,不保存发出请求的Web浏览器进程的任何状态信息。这有可能出现一个浏览器在短短几秒以内两次访问同一对象时,服务器进程不会由于已经给它发过应答报文而不接受第二期服务请求。因为Web服务器不保存发送请求的Web浏览器进程的任何信息,所以HTTP协议属于无状态协议(Stateless Protocol)

 

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'
  • cookie_name - 保存session id的Cookie的名称
  • cookie_domain - 保存session id的Cookie的domain信息
  • timeout - session的有效时间 ,以秒为单位
  • ignore_expiry - 若是为True,session就永不过时
  • ignore_change_ip - 若是为False,就代表只有在访问该session的IP与建立该session的IP彻底一致时,session才被容许访问。
  • secret_key - 密码种子,为session加密提供一个字符串种子
  • expired_message - session过时时显示的提示信息。

如何设置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中便可,确实省了不少事情呀,工具真的很须要也确实强大。

相关文章
相关标签/搜索