参考链接:
flask框架
Python Flask Web 框架入门
Flask全套知识点从入门到精通,学完可直接做项目
hello world
1
2
3
4
5
6
7
8
9
10
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World!'
if __name__ == '__main__':
app.run()
获取URL参数
列出所有参数
server:
1
2
3
4
5
6
7
8
9
10
11
12
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello_world():
return request.args.__str__()
if __name__ == '__main__':
app.run(port=5000, debug=True)
http://127.0.0.1:5000/?user=Flask&time&p=7&p=8 输出:
1
ImmutableMultiDict([('user', 'Flask'), ('time', ''), ('p', '7'), ('p', '8')])
较新的浏览器也支持直接在url中输入中文(最新的火狐浏览器内部会帮忙将中文转换成符合URL规范的数据),在浏览器中访问http://127.0.0.1:5000/?info=这是爱
输出:
1
ImmutableMultiDict([('info', '这是爱,')])
server:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello_world():
print(request.path)
print(request.full_path)
return request.args.__str__()
if __name__ == '__main__':
app.run(port=5000, debug=True)
http://127.0.0.1:5000/?info=这是爱 输出:
1
2
/
/?info=%E8%BF%99%E6%98%AF%E7%88%B1%EF%BC%8C
获取指定参数
server:
1
2
3
4
5
6
7
8
9
10
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello_world():
return request.args.get('info')
if __name__ == '__main__':
app.run(port=5000)
http://127.0.0.1:5000/?info=hello 输出:
1
helo
在URL参数中找到info。所以request.args.get(‘info’)返回Python内置的None,而Flask不允许返回None
server设置默认值:
1
2
3
4
5
6
7
8
9
10
11
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello_world():
r = request.args.get('info', 'hi')
return r
if __name__ == '__main__':
app.run(port=5000, debug=True)
http://127.0.0.1:5000/ 输出:
1
hi
处理多值
server:
1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello_world():
r = request.args.getlist('p') # 返回一个list
return str(r)
if __name__ == '__main__':
app.run(port=5000, debug=True)
http://127.0.0.1:5000/?user=Flask&time&p=7&p=8 输出:
1
['7', '8']
将python的参数传入到前端模版
server:
1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask,render_template
app=Flask(__name__)
@app.route('/about')
def about():
context={
"username": "李四"
}
return render_template("about.html", **context) # **context 传递字典
if __name__ == '__main__':
app.run(debug=True)
about_html:
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>关于我们</title>
</head>
<body>
<h1>我们是最棒的。</h1>
</body>
</html>
获取POST方法传送的数据
查看数据内容
server:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello world'
@app.route('/register', methods=['POST','GET']) #同时接受POST和GET方法
def register():
print(request.headers)
print(request.stream.read())
return 'welcome'
if __name__ == '__main__':
app.run(port=5000, debug=True)
client:
1
2
3
4
5
6
import requests
user_info = {'name': 'letian', 'password': '123'}
r = requests.post("http://127.0.0.1:5000/register", data=user_info)
print(r.text)
输出:
1
welcome
控制台输出:
1
2
3
4
5
6
7
8
9
Host: 127.0.0.1:5000
User-Agent: python-requests/2.19.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 24
Content-Type: application/x-www-form-urlencoded
b'name=letian&password=123'
解析POST数据
server:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from flask import Flask, request
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello world'
@app.route('/register', methods=['POST'])
def register():
print(request.headers)
# print(request.stream.read()) # 不要用,否则下面的form取不到数据
print(request.form)
print(request.form['name'])
print(request.form.get('name'))
print(request.form.getlist('name'))
print(request.form.get('nickname', default='little apple'))
return 'welcome'
if __name__ == '__main__':
app.run(port=5000, debug=True)
控制台输出:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Host: 127.0.0.1:5000
User-Agent: python-requests/2.19.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 24
Content-Type: application/x-www-form-urlencoded
ImmutableMultiDict([('name', 'letian'), ('password', '123')])
letian
letian
['letian']
little apple
equest.form会自动解析数据。 request.form[‘name’]和request.form.get(‘name’)都可以获取name对应的值。对于request.form.get()可以为参数default指定值以作为默认值
1
print(request.form.get('nickname', default='little apple'))
输出默认值
1
little apple
cilent:
1
2
3
4
5
6
import requests
user_info = {'name': ['letian', 'letian2'], 'password': '123'}
r = requests.post("http://127.0.0.1:5000/register", data=user_info)
print(r.text)
print(request.form.getlist(‘name’))输出:
1
[u'letian', u'letian2']
处理响应JS数据
server:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from flask import Flask, request
app = Flask("my-app")
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/add', methods=['POST'])
def add():
print(request.headers)
print(type(request.json))
print(request.json)
result = request.json['a'] + request.json['b']
return str(result)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=True)
client:
1
2
3
4
5
6
7
import requests
json_data = {'a': 1, 'b': 2}
r = requests.post("http://127.0.0.1:5000/add", json=json_data)
print(r.text)
client输出:
1
3
server输出:
1
2
3
4
5
6
7
8
9
10
11
Host: 127.0.0.1:5000
User-Agent: python-requests/2.19.1
Accept-Encoding: gzip, deflate
Accept: */*
Connection: keep-alive
Content-Length: 16
Content-Type: application/json
<class 'dict'>
{'a': 1, 'b': 2}
响应JSON时,除了要把响应体改成JSON格式,响应头的Content-Type也要设置为application/json
响应JSON数据方案1
server:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from flask import Flask, request, Response
import json
app = Flask("my-app")
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/add', methods=['POST'])
def add():
result = {'sum': request.json['a'] + request.json['b']}
return Response(json.dumps(result), mimetype='application/json')
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=True)
client:
1
2
3
4
5
6
7
8
import requests
json_data = {'a': 1, 'b': 2}
r = requests.post("http://127.0.0.1:5000/add", json=json_data)
print(r.headers)
print(r.text)
client输出:
1
2
{'Content-Type': 'application/json', 'Content-Length': '13', 'Server': 'Werkzeug/0.14.1 Python/3.6.5', 'Date': 'Thu, 06 Sep 2018 08:59:59 GMT'}
{"sum":3}
另外,如果需要服务器的HTTP响应头具有更好的可定制性,比如自定义Server,可以如下修改add()函数
1 2 3 4 5 6 @app.route('/add', methods=['POST']) def add(): result = {'sum': request.json['a'] + request.json['b']} resp = Response(json.dumps(result), mimetype='application/json') resp.headers.add('Server', 'python flask') return respclient将输出:
1 2 {'Content-Type': 'application/json', 'Content-Length': '10', 'Server': 'python flask', 'Date': 'Sat, 07 Jul 2018 05:26:40 GMT'} {"sum": 3}
响应JSON数据方案2
使用 jsonify 工具函数即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from flask import Flask, request, jsonify
app = Flask("my-app")
@app.route('/')
def hello_world():
return 'Hello World!'
@app.route('/add', methods=['POST'])
def add():
result = {'sum': request.json['a'] + request.json['b']}
return jsonify(result)
if __name__ == '__main__':
app.run(host='127.0.0.1', port=5000, debug=True)
上传文件
使用POST方法
server code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
from flask import Flask, request
from werkzeug.utils import secure_filename
import os
app = Flask(__name__)
# 文件上传的大小
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 #16MB
# 文件上传目录
app.config['UPLOAD_FOLDER'] = 'static/'
# 支持的文件格式
app.config['ALLOWED_EXTENSIONS'] = {'png', 'jpg', 'jpeg', 'gif'} # 集合类型
# 判断文件名是否是我们支持的格式
def allowed_file(filename):
return '.' in filename and \
filename.rsplit('.', 1)[1] in app.config['ALLOWED_EXTENSIONS']
@app.route('/')
def hello_world():
return 'hello world'
@app.route('/upload', methods=['POST'])
def upload():
upload_file = request.files['image']
if upload_file and allowed_file(upload_file.filename):
filename = secure_filename(upload_file.filename)
# 将文件保存到 static/uploads 目录,文件名同上传时使用的文件名
upload_file.save(os.path.join(app.root_path, app.config['UPLOAD_FOLDER'], filename))
return 'info is '+request.form.get('info', '')+'. success'
else:
return 'failed'
if __name__ == '__main__':
app.run(debug=True)
upload_file.save(path)用来将upload_file保存在服务器的文件系统中,参数最好是绝对路径,否则会报错
clint code
1
2
3
4
5
6
7
8
9
import requests
file_data = {'image': open('Andy.jpg', 'rb')}
user_info = {'info': 'Andy'}
r = requests.post("http://127.0.0.1:5000/upload", data=user_info, files=file_data)
print(r.text)
编码转换
server code
1
2
3
4
5
6
7
8
9
10
11
12
@app.route('/user/<username>')
def user(username):
print(username)
print(type(username))
return 'hello ' + username
@app.route('/user/<username>/friends')
def user_friends(username):
print(username)
print(type(username))
return 'hello ' + username
http://127.0.0.1:5000/user/andy http://127.0.0.1:5000/user/andy/friends 输出:
1
2
andy
<class 'str'>
- 加入类型转换器:
1 2 3 4 5
@app.route('/page/<int:num>') def page(num): print(num) print(type(num)) return 'hello world'
http://127.0.0.1:5000/page/1 输出:
1
2
1
<class 'int'>
- 3个默认转换器
1 2 3
int accepts integers float like int but for floating point values path like the default but also accepts slashes
server code
1
2
3
4
5
@app.route('/page/<int:num1>-<int:num2>')
def page(num1, num2):
print(num1)
print(num2)
return 'hello world'
http://127.0.0.1:5000/page/11-22 输出:
1
2
11
22
url_for生成链接
server code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from flask import Flask, url_for
app = Flask(__name__)
@app.route('/')
def hello_world():
pass
@app.route('/user/<name>')
def user(name):
pass
@app.route('/page/<int:num>')
def page(num):
pass
@app.route('/test')
def test():
print(url_for('hello_world'))
print(url_for('user', name='Andy'))
print(url_for('page', num=1, q='hadoop mapreduce 10%3'))
print(url_for('static', filename='uploads/01.jpg'))
return 'Hello'
if __name__ == '__main__':
app.run(debug=True)
http://127.0.0.1:5000/test 输出:
1
2
3
4
/
/user/Andy
/page/1?q=hadoop+mapreduce+10%253
/static/uploads/01.jpg
redirect重定向
server code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from flask import Flask, url_for, redirect
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello world'
@app.route('/test1')
def test1():
print('this is test1')
return redirect(url_for('test2'))
@app.route('/test2')
def test2():
print('this is test2')
return 'this is test2'
if __name__ == '__main__':
app.run(debug=True)
http://127.0.01:5000/test1 输出:
1
this is test2
Jinja2模板
Jinja2是Flask默认的模板引擎,它是基于Python的模板引擎,它的语法和Django模板引擎的语法类似。Jinja2模板引擎的语法请见 http://docs.jinkan.org/docs/jinja2/ 。
Jinja2中的控制语句
所有的控制语句都是放在中,并且由来进行结束,Jinja中常用的控制语句有if和for…in…,下面我们来看一下这两个控制语句的用法。
server code
1
2
3
4
5
6
7
8
9
10
11
12
from flask import Flask,render_template
import config
app=Flask(__name__)
app.config.from_object(config)
@app.route("/control")
def control():
context={
"age":17,
}
return render_template("control.html",**context)
if __name__ == '__main__':
app.run(debug=True)
control_html:
1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>控制语句</title>
</head>
<body>
<div>您刚成年!</div>
</body>
</html>
Jinja2模板过滤器
过滤器就是通过管道符( )进行使用的,例如就会返回name的长度,过滤器相当于是一个函数,把当前的变量传入到过滤器中,然后过滤器根据自己的功能,再返回相应的值,之后再将结果渲染到页面中。
不想让books以列表的形式展现,想要books以字符串的形式显示,每个项用逗号进行分割,可以使用join过滤器去处理
server code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask,render_template
import config
app=Flask(__name__)
app.config.from_object(config)
@app.route('/about')
def about():
context={
"username":"lisi",
"books":["红楼梦","西游记"]
}
return render_template("about.html",**context)
if __name__ == '__main__':
app.run(debug=True)
about_html
1
2
3
4
5
6
7
8
9
10
11
12
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>关于我们</title>
</head>
<body>
<h1>我们是最棒的。</h1>
<h1></h1>
<h1></h1>
</body>
</html>
输出:
1
2
3
我们是最棒的。4
['红楼梦','西游记']
红楼梦,西游记
将python的变量传入前端模板
server code
1
2
3
4
5
6
7
8
9
10
11
12
13
from flask import Flask,render_template
import config
app=Flask(__name__)
app.config.from_object(config)
@app.route('/about')
def about():
context={
"username":"李四"
}
return render_template("about.html",**context)
if __name__ == '__main__':
app.run(debug=True)
模版
模版继承
Flask中的模板可以继承,通过继承可以把模板中许多重复出现的元素抽取出来,放在父模板中,并且父模板通过定义block给子模板开一个口,子模板根据需要,再实现这个block,这样就可以实现模板的复用。
app_py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello world'
@app.route('/user')
def user():
user_info = {
'name': 'Andy',
'email': '123@aa.com',
'age': 21,
'github': 'https://github.com/andy-boy123'
}
return render_template('user_info.html', page_title='Andy\'s info', user_info=user_info)
if __name__ == '__main__':
app.run(port=5000, debug=True)
http://127.0.0.1:5000/user
网页源码:
1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<head>
<title>
Andy's info
</title>
</head>
<body>
name: Andy
email:123@aa.com
age: 21
github:https://github.com/andy-boy123
</body>
</html>
自定义404等页面
401未登录:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from flask import Flask, render_template_string, abort
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello world'
@app.route('/user')
def user():
abort(401) # Unauthorized 未授权
print('Unauthorized, 请先登录')
if __name__ == '__main__':
app.run(port=5000, debug=True)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from flask import Flask, render_template_string, abort
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello world'
@app.route('/user')
def user():
abort(401) # Unauthorized
@app.errorhandler(401)
def page_unauthorized(error):
return render_template_string('<h1> Unauthorized </h1><h2></h2>', error_info=error), 401
if __name__ == '__main__':
app.run(port=5000, debug=True)
page_unauthorized函数返回的是一个元组,401 代表HTTP 响应状态码。如果省略401,则响应状态码会变成默认的 200。
用户会话
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
from flask import Flask, render_template_string, \
session, request, redirect, url_for
app = Flask(__name__)
app.secret_key = 'F12Zr47j\3yX R~X@H!jLwf/T'
@app.route('/')
def hello_world():
return 'hello world'
@app.route('/login')
def login():
page = '''
<form action="" method="post">
<p>name: <input type="text" name="user_name" /></p>
<input type="submit" value="Submit" />
</form>
'''
return render_template_string(page)
@app.route('/do_login', methods=['POST'])
def do_login():
name = request.form.get('user_name')
session['user_name'] = name
return 'success'
@app.route('/show')
def show():
return session['user_name']
@app.route('/logout')
def logout():
session.pop('user_name', None)
return redirect(url_for('login'))
if __name__ == '__main__':
app.run(port=5000, debug=True)
app.secret_key用于给session加密。
在/login中将向用户展示一个表单,要求输入一个名字,submit后将数据以post的方式传递给/do_login,/do_login将名字存放在session中。
如果用户成功登录,访问/show时会显示用户的名字。此时,打开firebug等调试工具,选择session面板,会看到有一个cookie的名称为session。
/logout用于登出,通过将session中的user_name字段pop即可。Flask中的session基于字典类型实现,调用pop方法时会返回pop的键对应的值;如果要pop的键并不存在,那么返回值是pop()的第二个参数。
另外,使用redirect()重定向时,一定要在前面加上return。
进入http://127.0.0.1:5000/login,输入name,点击submit:
进入http://127.0.0.1:5000/show查看session中存储的name:
设置session的过期时间:
1
2
3
4
5
from datetime import timedelta
from flask import session, app
session.permanent = True
app.permanent_session_lifetime = timedelta(minutes=5)
使用cookie
Cookie是存储在客户端的记录访问者状态的数据。具体原理,请见 http://zh.wikipedia.org/wiki/Cookie 。 常用的用于记录用户登录状态的session大多是基于cookie实现的。
cookie可以借助flask.Response来实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
from flask import Flask, request, Response, make_response
import time
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'hello world'
@app.route('/add')
def login():
res = Response('add cookies')
res.set_cookie(key='name', value='1234', expires=time.time()+6*60)
return res
@app.route('/show')
def show():
return request.cookies.__str__()
@app.route('/del')
def del_cookie():
res = Response('delete cookies')
res.set_cookie('name', '', expires=0)
return res
if __name__ == '__main__':
app.run(port=5000, debug=True)
可以使用Response.set_cookie添加和删除cookie。expires参数用来设置cookie有效时间,它的值可以是datetime对象或者unix时间戳,此处使用的是unix时间戳
1
res.set_cookie(key='name', value='letian', expires=time.time()+6*60)
expire参数值表示cookie在6分钟后过期。 要删除cookie,只需要将expires设置为0即可
1
res.set_cookie('name', '', expires=0)
闪存系统 flashing system
闪存系统是一种临时存储数据的机制,它可以在一次请求中存储信息,然后在另一次请求中读取信息。闪存系统是基于session实现的,它的原理是将数据存储在session中,然后在下一次请求中将数据从session中删除。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from flask import Flask, flash, get_flashed_messages
import time
app = Flask(__name__)
app.secret_key = 'some_secret'
@app.route('/')
def index():
return 'hi'
@app.route('/gen')
def gen():
info = 'access at '+ time.time().__str__()
flash(info)
return info
@app.route('/show1')
def show1():
return get_flashed_messages().__str__()
@app.route('/show2')
def show2():
return get_flashed_messages().__str__()
if __name__ == "__main__":
app.run(port=5000, debug=True)
打开浏览器,访问http://127.0.0.1:5000/gen
1
access at 1694068052.5214832
打开浏览器,访问http://127.0.0.1:5000/gen
1
access at 1694068064.4610322
使用浏览器访问http://127.0.0.1:5000/show1
1
['access at 1694068052.5214832', 'access at 1694068064.4610322']
http://127.0.0.1:5000/show2
1
[]
flash分类(category):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from flask import Flask, flash, get_flashed_messages
import time
app = Flask(__name__)
app.secret_key = 'some_secret'
@app.route('/')
def index():
return 'hi'
@app.route('/gen')
def gen():
info = 'access at '+ time.time().__str__() #获取当前时间
flash('show1 '+info, category='show1') #分类1
flash('show2 '+info, category='show2') #分类2
return info
#只显示分类1
@app.route('/show1')
def show1():
return get_flashed_messages(category_filter='show1').__str__()
#只显示分类2
@app.route('/show2')
def show2():
return get_flashed_messages(category_filter='show2').__str__()
if __name__ == "__main__":
app.run(port=5000, debug=True)
蓝图和子域名
之前我们写的url和视图函数都是处在同一个文件,如果项目比较大的话,这显然不是一个合理的结构,而蓝图可以帮助我们实现这种需求。功能是将项目模块划分。
先写三个子模块,这三个文件全放在apps这个python packages里,结构如下
- Flask
- apps
- init_py
- book_py
- course_py
- user_py
- apps
init_py:
1
2
3
4
5
6
7
8
9
10
11
from flask import Flask
from apps.book import bp as book_bp
from apps.course import bp as course_bp
from apps.user import bp as user_bp
app=Flask(__name__)
app.register_blueprint(book_bp)
app.register_blueprint(course_bp)
app.register_blueprint(user_bp)
if __name__ == '__main__':
app.run(debug=True)
book_py:
1
2
3
4
5
6
from flask import Blueprint
# url_prefix就是设置url前缀
bp = Blueprint("book", __name__, url_prefix="/book")
@bp.route("/list")
def book_list():
return "图书列表"
course_py:
1
2
3
4
5
6
from flask import Blueprint
# url_prefix就是设置url前缀
bp = Blueprint("course", __name__, url_prefix="/course")
@bp.route("/list")
def course_list():
return "课程列表"
user_py:
1
2
3
4
5
6
from flask import Blueprint
# url_prefix就是设置url前缀
bp = Blueprint("user", __name__, url_prefix="/user")
@bp.route("/list")
def user_list():
return "用户列表"
访问http://127.0.0.1:5000/book/list 显示:
1
图书列表
其他也类似