【跟着Head First学python】5、构建一个web应用:来真格的

本章开始,事情会变得好玩,终于可以写出不是在控制台等地方运行的代码了。

1、准备工作:安装flask

flask是一个框架,提供了一组模块,用于构建服务器端的web应用。注意,flask比较简单,当然我们的应用也比较简单,更复杂的框架如Django暂时还用不到。对于我们这些初学者来说,flask足够用了。

使用pip安装flask框架。类似第4章。

2、检验flask是否正确安装

使用如下的代码完成检验:

from flask import Flask

app=Flask(__name__)

@app.route('/')
def hello()->str:
    return 'Hello word from Flask!'

app.run()

暂时先不用管它什么意思,直接打出来就好。有一点要注意,name左右两侧都是两个下划线,而不是一个。

将上述代码另存为hello_flask.py,单独放在一个文件夹中。

然后利用控制台进入该文件夹,运行该程序。注意,不要再idel中运行该文件,idel太小了,吃不消。运行如下:

默认的控制台在C盘,要想转移到D盘等,输入d:即可。

如上图所示,出现最后一行说明已经运行成功。flask内部有一个虚拟的服务器,在这个虚拟的服务器上运行该文件。要想访问该服务器,在浏览器中输入127.0.0.1:5000即可。

效果如下图:

也就是说,上述文件的功能是显示字符串。= =

那么现在可以转过头来仔细研究那几行代码都干了些什么。在注释中看方便点。

from flask import Flask #导入flask模块的Flask类

app=Flask(__name__) 
"""
创建一个Flask类型的对象,其中,参数__name__称为“dunder name”,双下划线称为dunder
这个__name__的值有Python解释器维护,当一个模块,也就是一个后缀名为py的文件被运行时,它会自动加载一些变量,__name__就是其中之一。它的值按如下规则确定:
现在有模块A和模块B,模块A中有函数C,那么,A模块有自己的__name__,B模块也有自己的__name__。
如果A模块调用自己的函数C,传入的参数为__name__,那么在C中打印__name__,将会显示__main__,表示C这个函数是在A中调用的。
如果B模块调用A的函数C,传入的参数同样为__name__,那么在C中打印__name__,将会显示B,表示C这个函数是在B中调用的。
因此,每个模块的__name__用于表明现在用这个模块的是哪个模块。
像上面这句代码,其中的__name__值应该为__main__,因为我们直接运行该代码。
"""
@app.route('/')
'''
以@开头的代码称为修饰符,用于调整一个现有函数的行为,无需修改函数本身。也就是说,使函数得到修饰。
当然,修饰符也可以用来修饰类,但更多的用于修饰函数,因此仍然叫它函数修饰符。
这里的route就是Flask模块提供的一个修饰符。它的用途就是允许你将一个URL路径,也就是参数,本文中是'/',与一个修饰符下面的一个python函数相连。
当一个指向'/'URL的请求到达服务器,route修饰符会安排服务器调用与之相连的函数,并等待捕获函数的返回值。捕获到返回值之后,将返回值传送给服务器,服务器会返回给浏览器。
这样,在函数中我们就不需要去实现服务器与客户端的通信工作了,专心实现逻辑即可。修饰符会为函数加入通信功能。
'''
def hello()->str:
    return 'Hello word from Flask!'

app.run()
'''
最后这行代码让Flask运行它的Web服务器。
'''

为了验证__name__的值是否为main,我们可以写出如下两个文件来验证:在同一文件夹中,新建两个模块(py文件),judgename和testjudge。其代码依次如下:

def judge(name):
    print(name)

judgename模块中定义了一个函数judge,其参数为name,函数功能是打印该参数。

from judgename import judge
judge(__name__)

testjudge模块中调用了judge函数,参数为__name__。

其效果如下:

说明__name__值的确为__main__,因为调用judge函数的就是testjudge,而我们直接命令testjudge运行,内有中间商赚差价。

也可以写第三个模块testother,其代码如下:

from testjudge import test
test()

从testjudge模块中调用test函数,因此需要修改testjudge如下:

from judgename import judge
def test():
    judge(__name__)

运行testother,结果如下:

分析可知,testother调用test函数,test函数调用judge函数,传入testjudge模块的__name__参数,然后在judgename函数中,运行judge函数,打印参数。显示为testjudge。也就是说,调用judge函数的是testjudge。这里直接运行的是testother,testjudge成了中间商。

现在问题来了,如果我在judgename模块中加入一行,打印__name__的值,会显示什么呢?是显示__main__,还是显示judgename呢?正常来讲,__name__的值就是它所在的模块的名字,但若是在本模块中运行,而不是import过去的,那应该是__main__,事实是这样吗?

我们更改judgename模块的代码如下:

def judge(name):
    print(name)
    print(__name__)

再次运行testother,效果如下:

上边那行依然是testjudge不变,下面那行却是judgename,而不是__main__,这说明,“__name__的值就是当前模块的名字”这一判断是正确的,“但若是在本模块中运行,而不是import过去的,那应该是__main__”这一判断是错误的。实际上,只有按下f5的那个模块中的__name__的值才是__main__,其他的__name__值都是本模块的名字。

在许多python代码中,均有如下的代码段:

if __name__=='__main__':
    #代码段A
else:
    #代码段B

其含义如下:若该模块直接运行,那么运行代码段A,若是import到其他模块,被其他模块调用,那么运行代码段B。

由于许多脚本是由python编写,因此存在直接运行和被调用两种情况。当代码被当做是脚本时,有一种行为方式;当代码被当做函数调用时,有另外一种行为方式。

3、练习

增加一个新功能,使得进入网页127.0.0.1:5000/search4时可以调用search4letters函数。

search4letters函数需要两个参数,但我们还没有学习如何进行交互,因此先输入参数如下:

第一个参数:'life,the universe, and everthing!'

第二个参数:'eiru,!'

注意,我们的search4letters函数的返回值是一个集合,然而web只能接受字符串格式的结果,因此需要用str函数进行转换。代码如下:

from flask import Flask
from vsearch import search4letters

app=Flask(__name__)

@app.route('/')
def hello()->str:
    return 'Hello word from Flask!'
@app.route('/search4')
def do_search()->str:
    return str(search4letters('life,the universe, and everthing!','eiru,!'))

app.run()

其效果如下:

可以看到,调用成功了。

但这并没有什么用,因为这网页毫无交互性。

4、构建html表单

html表单,就是用于描述想要的网页的样子的。要是写过Android,那以xml文件来类比应该不错。

这里的重点不是html该怎么写好看,做出来就可以。

需要两个网页,一个是用于输入,一个是用于显示结果。

网页需要有一个基模板,所有网页都按基模板的画风。于是一共需要有三个网页文件:基模板、输入网页、输出网页。

基模板代码如下:

<!doctype html>
<html>
	<head>
		<title>{{ the_title }}</title><!-- 这里的the_title是模板的一个参数 -->
		<link rel="stylesheet" href="static/hf.css"><!-- 这个hf.css定义了所有web页面的外观 -->
	</head>
	<body>
		{% block body %}
		<!-- 这个文件是一个模板,意思就是所有其他网页都按这个代码写 -->
		<!-- 在其他的网页文件中,这些代码不用再写一次,只要写这里两个body之间的代码就可以了 -->
		<!-- 这一部分称为命名块 -->
		{% endblock %}
	</body>
</html>

其他文件就只需要添加命名块部分就可以了。<!-- -->是注释,看上去是个颜文字,很可爱。

css文件用于定义整个外观,这个不用我们管。

所有的参数都用{}包围起来。

输入网页称为entry,代码如下:

{% extends 'base.html' %}<!-- 这说明这个网页继承了base基模板 -->
{% block body %}<!-- 这部分开始替代基模板中的命名块 -->

<h2>{{ the_title }}<h2>

<form method='POST' action='/search4'>
<table>
<p>Use this form to submit a search request:</p>
<tr><td>Phrase:</td><td><input name='phrase' type='TEXT' width='60'></td></tr>
<tr><td>Letters:</td><td><input name='letters' type='TEXT' value='aeiou'></td></tr>
</table>
<p>When you're ready, click this button:</p>
<p><input value='Do it!' type='SUBMIT'></p>
</form>

{% endblock %}

输出网页称为results,代码如下:

{% extends 'base.html' %}<!-- 这说明这个网页继承了base基模板 -->
{% block body %}<!-- 这部分开始替代基模板中的命名块 -->

<h2>{{ the_title }}<h2>

<p>You submitted the following data:</p>
<table>
<tr><td>Phrase:</td><td>{{ the_phrase }}</td></tr>
<tr><td>Letters:</td><td>{{ the_letters }}</td></tr>
</table>

<p>When "{{ the_phrase }}" is search for "{{ the_letters }}", the following results are returned:</p>
<h3>{{ the_results }}</h3>
<!-- 注意the_phrase、the_letters、the_results也是模板的参数,在呈现出来之前也需要给它们提供值-->

{% endblock %}

观察上述代码,参数有the_phrase、the_letters、the_results、the_title四个参数。这是我们的python代码需要提供的。

根据我们已经有的代码,python代码需要实现以下逻辑:

收集客户端的输入,处理输入,输出传送给客户端。还要把接受输入这个任务与entry这个html联系起来,把传送输出与results联系起来。

代码如下:

from flask import Flask, render_template,request,redirect
from vsearch import search4letters

app=Flask(__name__)

'''
@app.route('/')
def hello() -> '302':
    return redirect('/entry')
'''

@app.route('/')
@app.route('/entry')
def entry_page() -> 'html':
    return render_template('entry.html',
                           the_title='Welcome to search4letters on the web!')

@app.route('/search4',methods=['POST'])
def do_search() -> 'html':
    phrase=request.form['phrase']
    letters=request.form['letters']
    results=str(search4letters(phrase,letters))
    return render_template('results.html',
                           the_title='Here are your results',
                           the_phrase=phrase,
                           the_letters=letters,
                           the_results=results)

app.run(debug=True)

看第一行,render_template函数用于调用html文件,调用该函数之后,就会返回一个html串,这个html串叫串,他其实可以看做一个网页。也就是说,返回一个网页。另外,该函数也可以用于填补html中的参数。这样就把逻辑与html联系起来了。

request用于接收在form中的数据。form指的是html中的<form> </form>部分。看entry.html,就有form部分。request函数可以将这部分提取出来。于是可以得到用户的输入。

redirect函数用于重定向,也就是当访问一个url时,会转而访问另一个url。

接下来看主体部分。

首先是新建一个flask实例app,没什么好说的。

然后是当url为'/'或'entry'被访问时的行为。注意,可以选择让多个url指向一个行为,表面上来看就相当于一个网页俩网址。这样就不用重定向了。访问这两个url时,执行函数entry_page,该函数将调用render_template,返回一个html。也就是返回一个网页。看render_template函数的参数,有两个,一个是要调用的html的名字,这里是entry,另一个就是其中的参数the_title。这样,用户就可以收到一个网页了。

接下来是当url为'search4'被访问时的行为。注意,search4被访问,不是用户在地址栏中的访问,而是通过entry这个网页跳转到rsearch的。来分析一下entry的代码:

<form method='POST' action='/search4'>
<table>
<p>Use this form to submit a search request:</p>
<tr><td>Phrase:</td><td><input name='phrase' type='TEXT' width='60'></td></tr>
<tr><td>Letters:</td><td><input name='letters' type='TEXT' value='aeiou'></td></tr>
</table>
<p>When you're ready, click this button:</p>
<p><input value='Do it!' type='SUBMIT'></p>

这部分是要从客户端发送到服务器的。method='POST'表示使用的方法是POST,即向服务器发送数据。action='/search4'表示把数据发送到search4这个url。

最常用的方法有两个:GET和POST,前者用于向服务器请求数据,后者用于给服务器发送数据。此处用的是后者,把form部分发回去。可以看到,发送的有两个参数:phrase和letters。

然后再看python部分,当search4这个url被访问,将会调用do_search函数,这个函数也返回一个网页。注意该函数的修饰符,有一个method参数。一般对于url,默认只支持GET方法,也就是说,访问这个url,你只可以要东西,要想支持POST方法,就是给这个url东西,需要在修饰符中加入。另外注意一点,一旦在修饰符中有了method参数,默认的支持GET方法就不好用了,以method的为准。因此,search4这个url只支持POST方法。接下来看do_search函数如何运行。

首先,该函数会调用request,从form部分把两个变量提取出来。其次,调用search4letters得到结果,最后,调用render_template函数填充参数,返回网页。这样就得到了结果。

最后的app.run(debug)负责将这个实例运行起来。debug=True表示启动debug模式,更改python代码,改动可以实时体现出来。

总的来看,流程如下:

用户访问/entry的网址→调用entry_page函数→调用render_template函数→返回entry.html网页→用户输入phrase和letters→用户按下Do it按钮→form部分使用POST方法返回给服务器,访问/search4url→调用do_search函数→调用request函数,提取phrase和letters→调用search4letters函数生成结果→调用render_template函数→返回results.html网页

另外,要想整个webapp正常工作,文件结构如下:

app文件夹内有两个文件夹一个文件,两个文件夹分别为static,内有hf.css文件;templates,内有base、entry、results三个html文件;外面的一个文件是py文件。

 

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值