最近在读一篇文档,作者是一位使用多种语言开发复杂程序并且拥有十多年经验的软件工程师,曾经用 PHP, Ruby, Smalltalk 甚至 C++ 写过 web 应用,他认为,在所有这些中,Python/Flask 组合是最为自由的一种。
当然关于Python开发Web最好用的框架是什么,网上一直存在着各种各样的争论,这些争论对于我们本身并不重要。想要知道梨子的滋味就要亲口尝一尝(根红苗正),自己去体验才能感受到Flask与Django各自的魅力。
在使用一种语言或是一种框架时,我喜欢首先去体会一下名字,开发者给自己的作品命名时,总会蕴含这某些意义,当然像Java这种就不谈了。第一次看到Flask的logo时,我以为是一根辣椒,因为和《植物大战僵尸》中的形象也太相似了。后来查了字典(战五渣),是“烧瓶;火药筒”,大概有小体积大爆发的意味,很契合了。
下面回归正题。
获得对象
from flask import Flaskapp = Flask(__name__)@app.route('/')def hello_world(): return 'Hello World'if __name__ == '__main__': app.run() 复制代码
这是一个最简单的Demo。
执行流程为:从flask模块获取对象app,通过路由,执行方法,返回内容。
此时在浏览器访问(默认端口5000):127.0.0.1:5000/ ,可以看到国际惯例Helloworld的界面。
调试模式
值得一提的是,当我们在开发调试中需要反复修改代码,那么也意味着需要反复执行,这十分浪费时间并且容易让人烦躁。所以可以使用调试模式。
app.debug = Trueapp.run() #或者app.run(debug=True)复制代码
不过需要注意的是,这只适合在开发模式下使用,切不可用于生产环境,因为会造成极大的安全隐患。
路由
-
唯一URL:
@app.route('/hello')@app.route('/hello/')#这两种需要区分#@app.route('/hello/')#在使用这种尾部带斜线的url时,假如用户没有输入尾部/,也将访问到正确的页面#@app.route('/hello')#在使用这种尾部不带斜线的url时,假如用户在尾部输入了/,将返回404复制代码
这个规则似乎有点拗口,但其实也不能理解。优点是:
- 使得用户在遗忘尾斜线时,允许关联的 URL 接任工作,与 Apache 和其它的服务器的行为并无二异
- 保证了 URL 的唯一,有助于避免搜索引擎索引同一个页面两次。
如果实在记不清,最好的方法是破罐子破摔:统一不带尾部“/”
-
构造URL中的动态部分
@app.route('/var/')def var(name): return 'hello'+' '+name复制代码
这点就不赘述了,可以看一下演示效果:
- #####构造URL 使用url_for()方法来通过函数名反向生成url,看完以下代码就能很好理解了。
from flask import Flask, url_forapp = Flask(__name__)@app.route('/')def index(): pass@app.route('/login')def login(): pass@app.route('/user/')def profile(username): passwith app.test_request_context():print (url_for('index'))print (url_for('login'))print (url_for('login', next='/'))print (url_for('profile', username='Ji Hangyu'))复制代码
输出结果为:
//login/login?next=//user/Ji%20Hangyu复制代码
有人看到这里会很疑惑:这不是多此一举吗?我既然已经正向写出了路由及方法名,为什么还要反向去获取路径?需要用到的时候直接自己写不就好了吗?
这是一种比较直观的思路,待写完构造URL的三个优点时,就会理解这种写法的好处了:
- 反向构建通常比硬编码的描述性更好。更重要的是,它允许你一次性修改 URL, 而不是到处边找边改。
- URL 构建会转义特殊字符和 Unicode 数据,免去你很多麻烦。
- 如果你的应用不位于 URL 的根路径,url_for()会妥善处理这个问题。
模板渲染
大部分时候,在用户访问了一个URL的时候,我们都需要给他/她返回一个界面,我们当然不会用Python本身去渲染HTML,为此,Flask 配备了 模板引擎。
看完以下代码示例,相信你就能理解。
首先,我们创建“templates”文件夹用于保存模板。
Flask 会在 templates 文件夹里寻找模板。所以,如果你的应用是个模块,这个文件夹应该与模块同级;如果它是一个包,那么这个文件夹作为包的子目录:
#情况 1: 模块:/application.py/templates /hello.html#情况 2: 包:/application /__init__.py /templates /hello.html复制代码
- 不含参数示例 在程序执行:
@app.route('/redi/')def redi(): return render_template('hello.html')复制代码
- 再看另一个例子,加入动态参数:
@app.route('/redi2/')def redi2(name): return render_template('hello2.html',name=name)复制代码
GET和POST
请求方式不止这个两种,但是最常用的是这两种,如果对这两种不熟悉,可以先去查一下HTTP方法的资料,这里只演示在flask中的用法。
@app.route('/met',methods=['GET','POST'])def met(): if request.method=='GET': return '这是get方法' if request.method=='POST': return '这是post方法'复制代码
打开Postman这款软件(Web神器),模拟发送HTTP请求。
请求对象
下面我来模拟一个简单的登录操作。
首先是控制器:
@app.route('/login',methods=['POST','GET'])def login(): error=None if request.method=='POST': print (request.form['username']+' '+request.form['password']) if func.login_func.valid_login(request.form['username'], request.form['password']): return func.login_func.login_success(request.form['username']) else: error='Invalid username/password' return render_template('login_error.html',error=error)复制代码
可以看到执行流程:
- 获得请求
- 判断请求类型
- 获得登陆数据
- valid_login()方法验证登陆 4.1 若登陆成功,执行login_success()方法 4.2 若登录失败,添加失败信息,返回失败模板
下面是上述用到的两个方法:
def valid_login(username,password): if username=='admin' and password=='admin': return True复制代码
def login_success(username): return render_template('login_success.html',username=username)复制代码
下面使用Postman来模拟请求,看看能不能返回设想的结果。
文件
首先是上传文件的页面,记得在input标签中添加enctype="multipart/form-data"
和method="post"
,确保表单有提交文件的能力。
控制器:
@app.route('/upload_file',methods=['GET','POST'])def upload_file(): if request.method=='POST': f=request.files['the_file'] f.save('upload_files/test_file.txt') return 'success' else: return 'failure'复制代码
现在来尝试运行一下。
这时再看文件夹,已经保存了提交的文件。
这是简单的提交,至于安全方面的考虑,后续需要再学习。