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

利用web.py+html(bootstrap)+mysql实现了一个小型的设备管理系统,在这个过程中遇到很多问题,将问题及解决方案总结如下,有遇到类似问题的同学,希望可以帮到你们。

1、关于中文的编码方式,mysql+python+web.py+html的这个东西,从html页面输入中文提交insert到数据库,再从数据库中读取
内容到展示在html页面上,中文的乱码问题的处理,具体见虾米的博客中,写了这个处理方式,主要就是:
(1)各个部分的编码方式都保持唯一
(2)在mysqldb中的cursor.py的文件中的encode中的charset直接改成了'utf-8',这样就能将中文正确插入到数据库了
(3)然后在对数据库进行get操作的所有py文件的类方法中,conn时最后面增加一个charset="utf8"的参数,这样就能从数据库中
取出中文并显示正确了


2、今天遇到了在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"的提示;

range和xrange的区别:(在python 2.X下)

range的返回结果是一个list、xrange的返回结果是一个迭代器,两者都可以用于for循环,但是range因为会生成一整个list,所以占用空间要比xrange大,速度上也会比xrange要慢。

一般情况下循环都用range,但是如果是极大长度的数组就需要用xrange,生成器的意思是不预先分配数组长度的空间 是循环到哪 分配到哪

另外,针对这种遍历的操作,如果需要取元素内容的,直接用for one in ll

如果同时需要迭代变量和元素内容的,直接用for i, item in enumerate(ll),其中ll可以是list、dict、tuple

3、url的控制相关的需要总结(尤其是xx(d+)这样子的跳转控制)

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对应的数据信息

4、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页面上对应的数据显示


5、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语句即可。


6、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就可以得到你想要的效果


7、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操作即可


8、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


9、这里要把整个一套流程全部理清楚,具体走的流程是什么?一步一步是怎么走的?

web.py的整个流程?从通过在浏览器中输入http://localhost:8080点击enter之后的整个流程是怎样的?

答:

需要的多个端主要包括:

1、mysql数据库

2、python文件——主程序、model(实现对数据库的操作)、其他公共文件

3、template模板文件——html文件都放在一个templates的文件夹内,然后将文件夹的路径在主程序中声明,主程序就能找到对应的html文件

4、静态文件访问——在主程序的同目录下建一个新文件夹static,这个文件夹与templates文件夹平级,然后在static目录下建img文件夹放图片资源,js文件夹放js脚本,以及css文件夹,以及fonts文件夹等;具体的引入资源的路径如下所示:./static/img表示从当前html页面返回到上一级目录然后进入到static目录之后进入img目录找到正确的图片即可

img src="./static/img/why.jpg"

5、如何跳转——

首先需要针对/操作做一个index的处理;之后跳转到对应的类,之后针对具体的每一个控件其实都对应一个href的操作,这个直接写主程序中的urls的前面一项的内容,然后程序就能通过这个urls的前面一项找到其对应的后面一项,找到之后就会去对应的class查找确定走GET处理还是POST处理,然后再GET处理或者POST处理的最后一般会走一个render.xxx来跳转到某个特定的html或者是通过web.seeother()等提走到某个特定的html


10、数据库导入导出的方法

数据库导出——

通过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(当然你之前的数据库必须得是这个名字,否则找不到就不会覆盖恢复的)

11、部署到一台新机器上时,出现了提示"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之后,能够找到本地的数据库了,就正常了;


12、excel数据直接导入到sql中——这个要记录一下

问题——之前组内的设备都是直接记录在excel中的,需要从excel中直接导入到sql中

处理方法——最开始想了很多方法,比如把excel的数据先全部转成json或者xml,然后再自己写程序将json或者xml转成sql的insert语句,然后批量插入;后来很偶然的在excel的数据的菜单栏下看到了Mysql for Excel  Database的一项,当然是因为本地安装了数据库,然后后续就直接调整excel的格式,然后将excel的数据直接append到table中即可,确实省了很多事情呀,工具真的很需要也确实强大。

原文地址:https://www.cnblogs.com/keke-xiaoxiami/p/5229370.html