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中便可,確實省了不少事情呀,工具真的很須要也確實強大。

相關文章
相關標籤/搜索