【Python】[08]基於CGI的Web開發
開始web開發前,先來認識幾個概念:
CGI(Common Gateway Interface):稱為通用閘道器介面,是一個Internet標準。
官方解釋是生成動態內容的過程,這個動態過程大概是:web伺服器先找到所要執行的程式,然後執行找到的程式,再捕獲程式的輸出作為web響應,把它發回還在等待的web瀏覽器。這個程式被叫做CGI指令碼。
MVC(Model-View-Controller):模型-檢視-控制器模式,這是設計web應用應遵循的模式,他有助於把程式碼分解成易於管理的功能模組或者元件。
模型:儲存(以及有時處理)web應用資料的程式碼
試圖:格式化和顯示web應用使用者介面的程式碼
控制器:將wenbb應用“粘合”在一起並提供業務邏輯的程式碼
下面正式開始使用python編寫一個web應用
需求是:Kelly教練想要把他帶的運動員的成績放在網頁上,方便大家及時查詢成績
1、首先對運動員的成績進行資料建模
因為計算機處理程式時,只能識別和應用二進位制格式的資料,所以在啟動web應用時,先把要用到的文字檔案轉換成資料物件例項。比如,這裡需要先把教練的各學員的.txt資料,轉換成物件例項儲存在字典裡,這樣在web執行時,字典裡的資料就可以直接被web應用所使用。
import pickle from athletelist import AthleteList def get_coach_date(filename): #讀取教練的資料檔案try: with open(filename) as f: date=f.readline() templ=date. strip().split(".") return (AthleteList(templ.pop(0),templ.pop(0),templ)) except IOError as ioerror: print("File error:"+str(ioerror)) return(None) def put_to_store(files_list): #將各學員的文字檔案寫入字典,方便web應用執行 all_athletes={} for each_file in files_list: ath=get_coach_date(each_file) all_athletes[ath.name]=ath #將檔案中的資料迭代到字典中,以運動員的名字作為字典的鍵 try: with open("athletes.pickle","wb") as athf: pickle.dump(all_athletes,athf) #pickle.dump()函式是將資料儲存到磁碟的意思 except IOError as ioerror: print("File error(put_to_store):"+str(ioerror)) return(all_athletes) def get_from_store(): #獲取存入字典中的學員成績資料 all_athletes={} try: with open("athletes.pickle","rb") as athf: pickle.load(all_athletes,athf) #pickle.load()函式是從磁碟恢復資料 except IOError as ioerror: print("File error(get_from_store):"+str(ioerror)) return(all_athletes)
2、整理web應用執行程式碼的目錄
主要由以下幾個成分構成:
a、一個父資料夾,存放其他資料夾 b、一個date資料夾,用來放運動員資料 c、一個images資料夾,放圖片 d、templetes資料夾,放模板庫 e、最重要的cgi-bin資料夾,所有的程式碼都要放在這裡
3、需要一個執行程式碼的伺服器
python自帶的有個小型伺服器,這個伺服器包含在http.server庫模組中
用python構建一個伺服器時,必須執行一下程式碼:
from http.server import HTTPServer,CGIHTTPRequestHandler #匯入HTTP伺服器和CGI模組 port=8080 #指定一個埠 httpd=HTTPServer(('',port),CGIHTTPRequestHandler) #建立一個HTTP伺服器 print("啟動伺服器:"+str(httpd.server_port)) httpd.server_forever() #提示並啟動伺服器
在要執行程式碼的資料夾下,開啟終端視窗執行伺服器(shift+右鍵,執行終端視窗),輸入“.\+伺服器程式碼的py檔名”
這是啟動伺服器後得到的反饋:
然後再瀏覽器中,可以輸入:http://localhost:8080網址來訪問頁面了
4、CGI跟蹤日誌
import cgitb cgitb.enable()
放在要跟蹤的CGI的頭部,可以在瀏覽器視窗中看見詳細的異常資訊,可以快速準確地定位問題,如果沒有加這個CGI跟中的話,只能看見標準的異常輸出,需要自己去排查問題
5、@property 修飾符(修飾符前面會有一個@符號)
放在類方法前面,可以使類方法表現的像類屬性一樣,差不多意思就是,將類方法當作類屬性一樣使用,如:
最終完成的效果頁面如下所示:
這裡寫出來個BUG,運動員列表沒有顯示出來,且直接點選確定按鈕時,需要一個提示資訊,提示“需要選擇一個運動員”。
但我很久都沒找到原因,決定先掠過這次的BUG,等我找到原因再來更新。這裡放入我的程式碼,若是有前輩知道問題原因,還望不吝賜教
generate_list.py檔案:
1 import cgitb 2 cgitb.enable() 3 4 import athletemodel 5 import yate 6 import glob #glob模組可以向作業系統查詢檔名列表,這裡要查詢date資料夾中的各運動員檔案列表 7 date_files=glob.glob("date/*.txt") 8 athletes=athletemodel.put_to_store(date_files) 9 10 print(yate.start_response()) 11 print(yate.include_header("Kelly教練的運動員名單")) 12 print(yate.start_form("generate_timing_date.py")) 13 print(yate.para("請選擇一名運動員:")) 14 15 for each_athlete in athletes: 16 print(yate.radio_button("which_athlete",athletes[each_athlete].name)) 17 18 print(yate.end_form("確定")) 19 print(yate.include_footer({"首頁":"/index.html"}))
generate_timing_date.py檔案:
import cgi import athletemodel import yate athletes=athletemodel.get_from_store() form_date=cgi.FieldStorage() athlete_name=form_date["which_athlete"].value print(yate.start_respone()) print(yate.include_header("Kelly教練的時間資料")) print(yate.header("運動員:"+athlete_name+",生日是:"+athletes[athlete_name].dob)) print(yate.para("最快的三個時間為:")) print(yate.u_llist(athletes[athlete_name].top3)) print(yate.include_footer({"首頁":"index.html","返回上一頁":"generate_list.py"}))
yate.py檔案:
from string import Template def start_response(resp="text/html"): return('Content-type: ' + resp + '\n\n') def include_header(the_title): with open('templates/header.html') as headf: head_text = headf.read() header = Template(head_text) return(header.substitute(title=the_title)) def include_footer(the_links): with open('templates/footer.html') as footf: foot_text = footf.read() link_string = '' for key in the_links: link_string += '<a href="' + the_links[key] + '">' + key + '</a> ' footer = Template(foot_text) return(footer.substitute(links=link_string)) def start_form(the_url, form_type="POST"): return('<form action="' + the_url + '" method="' + form_type + '">') def end_form(submit_msg="Submit"): return('<p></p><input type=submit value="' + submit_msg + '"></form>') def radio_button(rb_name, rb_value): return('<input type="radio" name="' + rb_name + '" value="' + rb_value + '"> ' + rb_value + '<br />') def u_list(items): u_string = '<ul>' for item in items: u_string += '<li>' + item + '</li>' u_string += '</ul>' return(u_string) def header(header_text, header_level=2): return('<h' + str(header_level) + '>' + header_text + '</h' + str(header_level) + '>') def para(para_text): return('<p>' + para_text + '</p>')
athletemodel.py檔案
import pickle from athletelist import AthleteList def get_coach_data(filename): try: with open(filename) as f: data = f.readline() templ = data.strip().split(',') return(AthleteList(templ.pop(0), templ.pop(0), templ)) except IOError as ioerr: print('File error (get_coach_data): ' + str(ioerr)) return(None) def put_to_store(files_list): all_athletes = {} for each_file in files_list: ath = get_coach_data(each_file) all_athletes[ath.name] = ath try: with open('athletes.pickle', 'wb') as athf: pickle.dump(all_athletes, athf) except IOError as ioerr: print('File error (put_and_store): ' + str(ioerr)) return(all_athletes) def get_from_store(): all_athletes = {} try: with open('athletes.pickle', 'rb') as athf: all_athletes = pickle.load(athf) except IOError as ioerr: print('File error (get_from_store): ' + str(ioerr)) return(all_athletes)
(PS:書中說的head first python支援網站裡的資料檔案,我也是找了好久才找到,這裡記一下地址:http://examples.oreilly.com/0636920003434/)