Python初入门(七)(Head First Python 第七章 集成在一起)

本文介绍了Python初学者如何构建Web应用,遵循MVC模式,讲解了数据建模、Web请求与响应、CGI脚本以及如何创建用户界面。通过示例代码展示了如何使用Python内置HTTP服务器和CGI接口来动态生成HTML页面,以及如何利用CGI跟踪模块cgitb进行错误调试。
摘要由CSDN通过智能技术生成

Web应用

好的Web应用应当遵循模型-视图-控制器(Model-View-Controller)模式,即MVC模式,这有助于将Web应用的代码分解为易于管理的功能模块(或组件):
这里写图片描述

Web应用
在Web上运行的一个程序

Web请求
从Web浏览器发送到Web服务器

Web响应
从Web服务器发送到Web浏览器,作为对Web请求的响应。

为数据建模

Web服务器需要存储数据的一个副本,在这里数据就是文件。

启动这个Web应用时,需要把文本文件中的数据转换为AtheleteList对象实例,存储在一个字典中(按选手名索引),然后保存为一个pickle文件,将这个功能放入put_to_store()函数中。

这个Web应用运行时,pickle文件中的数据可以作为一个字典供应用使用,将这个功能放入get_from_store()函数中。



import pickle

#意思是从athletelist.py中导入AthleteList
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:
        #get_coach_data(each_file)打开文件列表中的每一个文件,
        #并将其转换成AthleteList对象。
        #最后,将AthleteList对象存入all_athletes字典。
        ath = get_coach_data(each_file)
        all_athletes[ath.name] = ath
    try:
        #打开athletes.pickle文件,将字典 all_athletes存入其中。
        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:
        #打开athletes.pickle文件,它是一个字典
        #然后,将其赋值给字典all_athletes。
        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)

dir() 函数不带参数时,返回当前范围内的变量、方法和定义的类型列表;带参数时,返回参数的属性、方法列表。如果参数包含方法dir(),该方法将被调用。如果参数不包含dir(),该方法将最大限度地收集参数信息。

使用dir()确认导入成功
这里写图片描述

创建一个要处理的文件列表,然后调用put_to_store()函数将文件列表中的数据转换为一个字典,存储在一个pickle中:
这里写图片描述

使用数据字典中现有的数据来显示各个选手的名字和出生日期
这里写图片描述

使用get_from_store()函数将腌制数据加载到另一个字典中,然后重复前面的代码显示名字和出生日期,确认得到预期的结果
这里写图片描述

其中athletelist.py的代码为:


class AthleteList(list):

    def __init__(self, a_name, a_dob=None, a_times=[]):
        list.__init__([])
        self.name = a_name
        self.dob = a_dob
        self.extend(a_times)

    @staticmethod
    def sanitize(time_string):
        if '-' in time_string:
            splitter = '-'
        elif ':' in time_string:
            splitter = ':'
        else:
            return(time_string)
        (mins, secs) = time_string.split(splitter)
        return(mins + '.' + secs)

    @property
    def top3(self):
        return(sorted(set([self.sanitize(t) for t in self]))[0:3])


查看界面

我们已经编写了能够正常工作的模型代码,现在来看视图代码,这回创建Web应用的用户界面(user interface,UI)。

从书中下载了一个YATE的代码,下面我们来看一下。

#从标准库的"string"模块导入"Template"类,它支持简单的字符串替换模板
from string import Template

#这个函数需要一个(可选的)字符串作为参数,用它来创建一个"Content-type"行
#参数缺省值是"text/html"
def start_response(resp="text/html"):
    return('Content-type: ' + resp + '\n\n')

#这个函数需要一个字符串作为参数,用在HTML页面最前面的标题中
#页面本身存储在一个单独的文件"templates/header.html"中,可以按需替换标题
#read() 方法用于从文件读取指定的字节数,如果未给定或为负则读取所有。
def include_header(the_title):
    #打开模板文件(HTML),读入文件
    with open('templates/header.html') as headf:
        head_text = headf.read()
    header = Template(head_text)
    #换入所提供的标题
    return(header.substitute(title=the_title))

#与"include_header"函数类似,这个函数使用一个字符串作为参数,来创建一个HTML页面尾部
#页面本身存储在一个单独的文件'templates/footer.html'中,参数用于动态地创建一组HTML链接标记
#参数应当是一个字典
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)
    #换入所体统的HTML链接字典
    return(footer.substitute(links=link_string))

#这个函数返回表单最前面的HTML,允许调用者制定URL(表单数据将发送到这个URL)
#还可以制定要使用的方法(POST或GET)
def start_form(the_url, form_type="POST"):
    return('<form action="' + the_url + '" method="' + form_type + '">')

#这个函数返回表单末尾的HTML标记,同时还允许调用者定制表单"submit"按钮的文本
def end_form(submit_msg="Submit"):
    return('<p></p><input type=submit value="' + submit_msg + '"></form>')

#给定一个单选按钮名和值,创建一个HTML单选钮(通常包括在一个HTML表单中)
def radio_button(rb_name, rb_value):
    return('<input type="radio" name="' + rb_name +
                             '" value="' + rb_value + '"> ' + rb_value + '<br />')

#给定一个项列表,这个函数会把该列表转换为一个HTML无序列表
#一个简单的for循环就可以完成全部工作,每次迭代会想ul元素添加一个li元素
def u_list(items):
    u_string = '<ul>'
    for item in items:
        u_string += '<li>' + item + '</li>'
    u_string += '</ul>'
    return(u_string)

#创建并返回一个HTML标题标记(H1等),默认为H2,header_text参数是必须的
def header(header_text, header_level=2):
    return('<h' + str(header_level) + '>' + header_text +
           '</h' + str(header_level) + '>')

#用HTML段落标记包围一个文本段(一个字符串)
def para(para_text):
    return('<p>' + para_text + '</p>') 

我们可以看到
def start_response(resp=”text/html”):
return(‘Content-type: ’ + resp + ‘\n\n’)
中,resp的默认缺省参数是”text/html”,即如果不传参resp是”text/html”,记住这种用法。

首先测试start_response(0函数。CGI标准指出,每一个Web响应都必须有一个首部行来请求中包含的数据类型,这可以用start_response()来控制:
这里写图片描述

include_header()函数生成一个Web页面的开始部分,允许定制页面的标题:
这里写图片描述

include_footer()函数会生成一个Web页面末尾的HTML,并提供链接(如果已经提供一个链接字典)。倘若字典为空,就不会包含链接HTML:
这里写图片描述

start_form()和end_form()函数会简历一个HTML表单,并用参数(如果提供有参数)来调整所生成的HTML的内容:
这里写图片描述

HTML单选按钮可以用radio_button()函数来创建
这里写图片描述

用u_list()函数可以创建无序列表:
这里写图片描述

header()函数允许快速建立选定级别的HTML标题(默认为2)
这里写图片描述

para()函数会把一个文本块包围在HTML段落标记中间
这里写图片描述


控制你的代码

下面是Head First Labs推荐的一个文件夹结构
这里写图片描述


CGI让Web服务器运行程序

通用网关接口(Comman Gateway Interface,CGI)是一个Internet标准,允许Web服务器运行一个服务器端程序,成为CGI脚本。

一般地,CGI脚本都放在一个名为cgi-bin的特殊文件夹下,这样Web服务器才能知道在哪里查找CGI脚本。

Python提供了它自己的Web服务器,这个Web服务器包含在http.server库模块中。在我们下载的内容中,有一个名为simplehttpd.py的文件,它提供了一个支持CGI的Web服务器。

#导入HTTP服务器和CGI模块
from http.server import HTTPServer, CGIHTTPRequestHandler

#指定一个端口
port = 8080

#创建一个HTTP服务器
httpd = HTTPServer(('', port), CGIHTTPRequestHandler)
#显示一个友好的信息,并启动服务器
print("Starting simple_httpd on port: " + str(httpd.server_port))
httpd.serve_forever()

用Python构建一个Web服务器必须有这5行代码。


显示选手列表

下面创建一个generate_list.py的程序,Web服务器执行这个程序时,会动态生成一个HTMLWeb页面。目标是建立一个CGI脚本,从而生成目标HTML页面。

#利用glob模块可以想操作系统查询一个文件名列表
import glob

#导入之前创建的athletemodel
import athletemodel
import yate

data_files = glob.glob("data/*.txt")
athletes = athletemodel.put_to_store(data_files)

#总是从一个Content_type行开始
print(yate.start_response())
#开始生成web页面,提供一个合适的标题
print(yate.include_header("Coach Kelly's List of Athletes"))
#开始生成表单,提供要链接的服务器端程序的名。
print(yate.start_form("generate_timing_data.py"))
#一行文字
print(yate.para("Select an athlete from the list to work with:"))
#为各个选手分别生成一个单选钮
for each_athlete in athletes:
    print(yate.radio_button("which_athlete", athletes[each_athlete].name))
#创建一个提交按钮
print(yate.end_form("Select"))
print(yate.include_footer({"Home": "/index.html"}))

glob是python自己带的一个文件操作相关模块,用它可以查找符合自己目的的文件,就类似于Windows下的文件搜索,而且也支持通配符。它的主要方法就是glob,该方法返回所有匹配的文件路径列表,该方法需要一个参数用来指定匹配的路径字符串(绝对路径或相对路径)。

这里我们可以看到这个CGI脚本会调用yate,来生成我们所需的html页面。

测试这个代码
这里写图片描述

我们已经在计算机的端口8080运行了Web服务器,所以需要在浏览器中使用下面的地址http://localhost:8080/
这里写图片描述
查看index.html的源代码可以发现当点击timing data时,会跳转到 href=”cgi-bin/generate_list.py”。

这里写图片描述
而generate_list.py会生成我们所需要的HTML页面。

可以看到我们的web服务器开始工作,它会把它处理的所有web请求记录下来。
这里写图片描述
web服务器记录了web请求要运行”generate_list.py”CGI脚本



创建另一个CGI脚本


我们发现代码中
print(yate.start_form(“generate_timing_data.py”))
这是开始生成表单,提供要链接的服务器端程序的名。接下来我们完成这个代码:

#导入Python的CGI模块来访问表单数据,这也是标准库中的一个模块
import cgi

#Python的标准库提供了一个CGI跟踪模块(名为cgitb)。启用这个模块时,会在Web浏览器上显示详细的错误信息。这些信息可以帮助你找出CGI哪里出了问题。改正错误并且CGI正常工作后,在关闭CGI跟踪。
import cgitb
cgitb.enable()

import athletemodel
import yate

#调用athletemodel中的方法,获取pickle中的数据
athletes = athletemodel.get_from_store()

#访问你所选的数据,得到表单数据
#再得到表单数据中的单选按钮数据'which_athlete'
form_data = cgi.FieldStorage()
athlete_name = form_data['which_athlete'].value

#生成所需的HTML页面
print(yate.start_response())
print(yate.include_header("Coach Kelly's Timing Data"))    
print(yate.header("Athlete: " + athlete_name + ", DOB: " +
                      athletes[athlete_name].dob + "."))
print(yate.para("The top times for this athlete are:"))
print(yate.u_list(athletes[athlete_name].top3))
#两个链接
print(yate.include_footer({"Home": "/index.html",
                           "Select another athlete": "generate_list.py"}))

随便选择一个列表项并单击select按钮
这里写图片描述
可以看到data里面的数据。


启用CGI跟踪来帮助解决错误

Python的标准库提供了一个CGI跟踪模块(名为cgitb)。启用这个模块时,会在Web浏览器上显示详细的错误信息。这些信息可以帮助你找出CGI哪里出了问题。改正错误并且CGI正常工作后,在关闭CGI跟踪。

调用方法如下
import cgitb
cgitb.enable()

我们尝试在选择页面的时候,不选择任何单选按钮就按select按钮,会出现如下情况
这里写图片描述
你可以发现CGI跟踪模块会努力准确地找出代码中哪里出现了问题。

值得注意的是,print(yate.u_list(athletes[athlete_name].top3))在这里,把top3()方法看成了一个类属性而不是一个类方法,因为在AtheleteList中,top3()方法被指定为一个类属性。
这里写图片描述
@property是一个修饰符,可以使类方法表现得像是一个类属性。

当print(yate.u_list(athletes[athlete_name].top3()))这样调用时,CGI跟踪输出指出AthleteList代码中top3()方法的使用有一个错误。
这里写图片描述
这里写图片描述

如果不希望这样,可以将
这里写图片描述
中的@property去掉,这样top()就是类中的一个方法了。

本文中的代码请到这里下载
head first python源码

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值