1. 程式人生 > >【Python】[08]基於CGI的Web開發

【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>&nbsp;&nbsp;&nbsp;&nbsp;'
    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/)