学习
文章目录
代码仓库:https://github.com/LenkyAndrews/Flask-Learning
1.简介
Flask定制小程序开发是目前十分流行的web框架,采用Python定制小程序开发编程语言来实现相关功能。定制小程序开发它被称为微框架(microframework),“微”是指Flask定制小程序开发旨在保持代码简洁且易于扩展,Flask定制小程序开发框架的主要特征是核心定制小程序开发构成比较简单,定制小程序开发但具有很强的扩展性和兼容性。
Flask主要包括Werkzeug
和Jinja2
定制小程序开发两个核心函数库,定制小程序开发它们分别负责业务处理定制小程序开发和安全方面的功能,定制小程序开发这些基础函数为web定制小程序开发项目开发过程提供了丰定制小程序开发富的基础组件。
2.安装
定制小程序开发使用以下指令安装:
pip install flask
- 1
定制小程序开发输入指令查看版本
import flaskprint(flask.__version__)# 2.0.1
- 1
- 2
- 3
3.定制小程序开发最小的应用
# 导入Flask类库from flask import Flask# 定制小程序开发创建应用实例app = Flask(__name__)# 视图函数(路由)@app.route("/")def hello_world(): return "Hello, World!"# 启动服务if __name__ == '__main__': app.run(debug = True)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
-
首先导入了 Flask类。
-
接着创建一个该类的实例。第一个参数是应用模块或者包的名称。
__name__
是一个适用于大多数情况的快捷方式。 -
然后使用 route()装饰器来告诉 Flask 触发函数 的 URL 。
app.route(rule, options)
- 1
rule 参数表示与该函数的URL绑定
options 是要转发给基础Rule对象的参数列表。 -
函数返回需要在浏览器中显示的信息。
保存为app.py
,一般不要使用 flask.py
作为应用名称,这会与 Flask 本身发生冲突。服务启动默认使用5000端口,在浏览器中打开 http://127.0.0.1:5000/ ,可以看到 Hello World! 字样。
app.run(host, port, debug, options)
- 1
参数 | 说明 | 默认值 |
---|---|---|
host | 主机名,设置为“0.0.0.0”以使服务器在外部可用 | 127.0.0.1(localhost) |
port | 端口号 | 5000 |
debug | 调试模式,代码更新会自动重启服务,提供一个调试器来跟踪应用程序中的错误 | False |
options | 要转发到底层的Werkzeug服务器的参数 |
4.路由
现代Web框架使用路由技术来帮助用户记住应用程序URL。Flask中的route()
装饰器用于将URL绑定到函数,例如:
@app.route('/hello')def hello_world(): return 'hello world'
- 1
- 2
- 3
URL /hello
规则绑定到hello_world()函数,如果访问http://localhost:5000/hello,hello_world()函数的输出将在浏览器中呈现。
5.变量规则
通过把 URL 的一部分标记为 <variable_name>
就可以在 URL 中添加变量,标记的部分会作为关键字参数传递给函数。
from flask import Flaskapp = Flask(__name__)@app.route('/hello/<username>')def hello(username): return f'Hello {username}!'if __name__ == '__main__': app.run(debug = True)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
在上面的例子中,在浏览器中输入http://localhost:5000/hello/Kint,则Kint
将作为参数提供给 hello()函数,即username的值为Kint
。在浏览器中的输出如下
Hello Kint!
- 1
通过使用 <converter:variable_name>
,可以选择性的加上一个转换器,为变量指定规则。
转换器 | 说明 |
---|---|
string | (缺省值) 接受任何不包含斜杠的文本 |
int | 接受正整数 |
float | 接受正浮点数 |
path | 类似 string ,但可以包含斜杠 |
完整示例:
from flask import Flaskfrom markupsafe import escapeapp = Flask(__name__)@app.route('/hello/<username>')def hello(username): return f'Hello {username}!'@app.route('/post/<int:post_id>')def show_post(post_id): return f'Post {post_id}'@app.route('/path/<path:path>')def show_path(path): return f'path {escape(path)}'if __name__ == '__main__': app.run(debug=True)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
在浏览器中输入http://localhost:5000/integer/56,输出如下
Integer: 56
- 1
在浏览器中输入http://localhost:5000/path/templates/index.html,输出如下
path: templates/index.html
- 1
6.URL构建
url_for()
函数用于构建指定函数的 URL。它把函数名称作为第一个参数。它可以接受任意个关键字参数,每个关键字参数对应 URL 中的变量,未知变量 将添加到 URL 中作为查询参数。
from flask import Flask, request, redirectfrom flask import url_forapp = Flask(__name__)@app.route('/')def index(): return 'index'@app.route('/login')def login(): print(f"in login function, request.values: {request.values}") return 'login'@app.route('/user/<username>')def profile(username): return f'{username}\'s profile'with app.test_request_context(): print(url_for('index')) print(url_for('login')) print(url_for('login', next='/')) print(url_for('profile', username='John Doe'))
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
url_for('login', next='/')
会生成对应的URL,但next
并不是login函数的关键字参数,故作为查询参数,与GET请求发送的数据类似,可以在request.values中查询到CombinedMultiDict([ImmutableMultiDict([('next', '/')])])
输出如下:
//login/login?next=%2F/user/John%20Doe
- 1
- 2
- 3
- 4
7.HTTP方法
Web 应用使用不同的 HTTP 方法处理 URL ,HTTP方法如下(加粗的方法较为常见):
方法 | 说明 |
---|---|
GET | 以未加密的形式将数据发送到服务器,最常见的方法 |
HEAD | 和GET方法相同,但没有响应体 |
POST | 用于将HTML表单数据发送到服务器,POST方法接收的数据不由服务器缓存 |
PUT | 用上传的内容替换目标资源的所有当前表示 |
DELETE | 删除由URL给出的目标资源的所有当前表示 |
默认情况下,Flask路由只回应 GET
请求。可以使用 route()
装饰器的 methods
参数来处理不同的 HTTP 方法。
将以下脚本另存为welcome.html,放在templates文件夹中。
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>HTTP方法</title></head><body> <form action = "http://localhost:5000/login" method = "post"> <p>Enter Name:</p> <p><input type = "text" name = "username" /></p> <p><input type = "submit" value = "submit" /></p> </form></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
flask代码如下:
from flask import Flask, redirect, url_for, request, render_templateapp = Flask(__name__)@app.route('/')def index(): return render_template('welcome.html')@app.route('/welcome/<name>')def welcome(name): return f'welcome {name}'@app.route('/login', methods=['POST', 'GET'])def login(): if request.method == 'POST': user = request.form['username'] return redirect(url_for('welcome', name=user)) else: user = request.args.get('username') # GET方法获取数据,args是包含表单参数对及其对应值对的列表的字典对象。 return redirect(url_for('welcome', name=user))if __name__ == '__main__': app.run(debug=True)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
如果请求方式是POST方法,则通过以下代码获得从表单数据获得的“username”参数的值:
user = request.form['username']
- 1
如果请求方式是GET方法,则通过以下代码获得从表单数据获得的“username”参数的值:
user = request.args.get('username')
- 1
args
是包含表单参数对及其对应值对的列表的字典对象,与’username’参数对应的值将传递到/welcome
URL中,即
/login?username=Kint
- 1
8.文件存放
flask项目中,static
文件夹中存放CSS 和 JavaScript文件,templates
文件夹中存放html文件。app.py与templates和static文件夹同级目录。
/app.py/templates /index.html/static /style.css /jquery.js
- 1
- 2
- 3
- 4
- 5
- 6
果然想要更改路径可以在初始化Flask实例时进行修改
app = Flask(__name__, static_folder='', template_folder='')
- 1
9.渲染模板
使用 render_template()
方法可以渲染模板,提供模板名称和需要作为参数传递给模板的变量。
在templates文件夹下创建render_template.html文件,代码如下:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>render_template</title></head><body> <h2>渲染模板</h2> {{ my_int }} <br> {{ my_str }} <br> {{ my_list }} <br> {{ my_dict }} <hr> <h2>列表数据获取</h2> {{ my_list[0] }} <br> {{ my_list.1 }} <hr> <h2>字典数据获取</h2> {{ my_dict['name'] }} <br> {{ my_dict.age }} <hr> <h2>算术运算</h2> {{ my_list.0 + 10 }} <br> {{ my_list[0] + my_list.1 }}</body></html>
- 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
flask代码如下:
from flask import Flask, render_templateapp = Flask(__name__)@app.route('/')def index(): int_ = 1024 str_ = 'Hello World!' list_ = [1, 2, 3, 4, 5] dict_ = {'name': 'Kint', 'age': 23} # render_template方法:渲染模板 # 参数1: 模板名称 参数n: 传到模板里的数据 return render_template('render_template.html', my_int=int_, my_str=str_, my_list=list_, my_dict=dict_)if __name__ == '__main__': app.run(debug=True)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
效果如下:
Flask 是使用 Jinja2 这个模板引擎来渲染模板,更多内容,详见官方 。
10.Request对象
部分属性如下(加粗的较为常见):
属性 | 说明 |
---|---|
method | 请求方法,比如GET、POST |
data | 以字符串的形式存储请求的数据 |
files | 上传的文件,类型为MultiDict |
args | 解析URL中提交的参数,类型为MultiDict |
form | 上传的表单数据,类型为MultiDict |
values | 包含args和form的数据,类型为CombineMultiDict |
json | 解析JSON数据,如果没有则返回None |
cookies | 包含客户端传输的所有 cookies ,类型为MultiDict |
headers | 请求头信息,类似字典,可通过关键词获取对应得信息 |
在templates文件夹下创建Request.html文件,代码如下:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>Request</title></head><body> <form action="/login?a=1&b=2" method="post"> <p>账号: <input type="text" name="username"></p> <p>密码: <input type="password" name="password"></p> <input type="submit"> </form></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
flask代码如下:
from flask import Flask, request, render_templateimport jsonapp = Flask(__name__)@app.route('/')def index(): return render_template("Request.html")@app.route('/login', methods=["GET", "POST"])def login(): print('request.method:', request.method) print('request.data:', request.data) print('request.request.args:', request.args) print("request.request.args.get('b'):\", request.args.get('c')) print('request.form:', request.form) print("request.request.form.get('password'):\", request.form.get('password')) print('request.values:', request.values) print('request.json:', request.json) print('request.cookies:', request.cookies) print('request.headers:', request.headers) return json.dumps(request.form) # 将MultiDict数据处理为JSON数据if __name__ == '__main__': app.run(debug=True)
- 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
效果如下:
后端输出如下:
以request.form为例,在获取MultiDict的值时,推荐使用request.form.get(key)
而不直接使用request.form[key]
,前一种方式可以避免关键词缺失而导致的KeyError。
获取各种路径,本例中的URL为http://127.0.0.1:5000/login?a=1&b=2
print('url: ', request.url)print('base_url: ', request.base_url)print('host: ', request.host)print('host_url: ', request.host_url)print('path: ', request.path)print('full_path: ', request.full_path)
- 1
- 2
- 3
- 4
- 5
- 6
输出为:
url: http://127.0.0.1:5000/login?a=1&b=2base_url: http://127.0.0.1:5000/loginhost: 127.0.0.1:5000host_url: http://127.0.0.1:5000/path: /loginfull_path: /login?a=1&b=2
- 1
- 2
- 3
- 4
- 5
- 6
11.Cookie
Cookie以文本文件的形式存储在客户端的计算机上。其目的是记住和跟踪与客户使用相关的数据,以获得更好的访问者体验和网站统计信息。比如网站为了辨别用户身份、进行会话跟踪需要把一些数据 (例如:登录状态、用户名称) 储存在用户本地终端上,这些数据被称为 Cookie。
Request对象包含Cookie的属性,它是所有Cookie变量及其对应值的字典对象。
在Flask中,对Cookie的处理步骤为:
- 使用Response对象的set_cookie()方法设置cookie
- 通过request.cookies的方式获取cookies,返回的是一个字典
- 使用Response对象的delete_cookie()方法设置cookie
在templates文件夹下创建get_cookie.html文件,代码如下:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>获取cookie</title></head><body> <h2>在服务端获取cookie: <b>{{cookie}}</b></h2></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
在templates文件夹下创建show_cookie_by_JQuery.html文件,代码如下:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>设置cookie</title> <script src="../static/jquery-3.6.0.js" type="text/javascript" charset="utf-8"></script></head><body> <h2>在服务端设置cookie</h2> <h2>在客户端通过JQuery读取cookie: <b id='cookie'></b></h2></body><script> $('#cookie').text(document.cookie);</script></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
flask代码如下:
from flask import Flask, request, Response, render_template, make_response, redirect, url_forapp = Flask(__name__)@app.route('/')def index(): return redirect(url_for('set_cookie')) # 重定向响应的URL# 获取cookie@app.route('/get_cookie')def get_cookie(): cookie = request.cookies.get('user') # 获取关键字为user对应cookie的值 print(cookie) return render_template('get_cookie.html', cookie=cookie)# 设置cookie@app.route('/set_cookie')def set_cookie(): html = render_template('show_cookie_by_JQuery.html') response = make_response(html) # 设置响应体 # response = Response(html) response.set_cookie('user', 'Kint') return response# 删除cookie@app.route('/del_cookie')def del_cookie(): html = render_template('show_cookie_by_JQuery.html') response = Response(html) response.delete_cookie('user') return responseif __name__ == '__main__': app.run(debug=True)
- 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
在set_cookie()函数中,首先渲染模板文件show_cookie_by_JQuery.html,通过make_response()方法设置响应体并返回Response对象,然后设置cookie。以下两种写法作用相同:
response = make_response(html) # 设置响应体response = Response(html)
- 1
- 2
在浏览器中输入http://127.0.0.1:5000/set_cookie,可以看到在响应头信息中已经设置好cookie的数据了。
在浏览器中输入http://127.0.0.1:5000/get_cookie,可以看到请求头信息中cookie的数据,并且将cookie传输到服务端上。
在浏览器中输入http://127.0.0.1:5000/del_cookie,此时响应体信息中已经没有user
对应的cookie值了。
12.会话Session
与Cookie不同,Session数据存储在服务器上,用来存储用户的信息 (例如登录状态、用户名称)。Session 有一个唯一标识 ID,对应一个用户,在服务端使用 ID 可以查找到对应用户的数据,故Flask应用程序需要一个定义的密钥SECRET_KEY。
以下代码简单实现判断用户是否登录功能:
from flask import Flask, redirect, url_for, request, sessionapp = Flask(__name__)app.secret_key = 'abc'@app.route('/')def index(): if 'user' in session: # 获取会话中的用户信息 user = session.get('user') return '登录用户是:' + user + '<br>' + "<b><a href = '/logout'>点击这里注销</a></b>" return "<h3>暂未登录</h3><br>" + "<a href = '/login'></b>点击这里登录</b></a>"@app.route('/login', methods=['GET', 'POST'])def login(): if request.method == 'POST': # 设置会话中的用户信息 session['user'] = request.form['user'] return redirect(url_for('index')) return ''' <form action="" method="post"> <p><input type="text" name="user"/></p> <p><input type="submit" value="登录"/></p> </form> '''@app.route('/logout')def logout(): # 删除会话中的用户信息 session.pop('user') return redirect(url_for('index'))if __name__ == '__main__': app.run(debug=True)
- 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
效果如下:
实例1.上传表单数据
文件结构如下:
/app.py/templates /login.html /result.html
- 1
- 2
- 3
- 4
app.py代码如下:
from flask import Flask, render_template, requestapp = Flask(__name__)@app.route('/')def student(): return render_template('login.html')@app.route('/result',methods = ['POST', 'GET'])def result(): if request.method == 'POST': result = request.form #拿到前端传输的表单数据 return render_template("result.html",result = result)if __name__ == '__main__': app.run(debug = True)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
在templates文件夹下创建login.html文件,代码如下:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>实例1.上传表单数据—登录页面</title></head><body> <form action="http://localhost:5000/result" method="POST"> <p>Name <input type = "text" name = "Name" /></p> <p>Email <input type = "text" name = "Email" /></p> <p><input type = "submit" value = "submit" /></p> </form></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
在templates文件夹下创建result.html文件,代码如下:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>实例1.上传表单数据-结果页面</title></head><body> <table border = 1> {% for key, value in result.items() %} <tr> <th> {{ key }} </th> <td> {{ value }}</td> </tr> {% endfor %} </table></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
实例2.上传下载文件
文件结构如下:
/app.py/templates /index.html /upload.html/upload /download_sample.txt
- 1
- 2
- 3
- 4
- 5
- 6
app.py代码如下:
from flask import Flask, render_template, request, send_from_directoryfrom werkzeug.utils import secure_filenameimport osapp = Flask(__name__)app.config['UPLOAD_FOLDER'] = 'upload/'app.config['MAX_CONTENT_LENGTH'] = 16 * 1000 * 1000 # 上传文件大小限制为16M,如果超过会抛出异常@app.route('/')def upload_file(): return render_template('index.html')@app.route('/upload', methods=['GET', 'POST'])def upload(): if request.method == 'POST': f = request.files['file'] print(request.files) # secure_filename检查客户端上传的文件名,确保安全,注意文件名称不要全中文!!! f.save(os.path.join(app.config['UPLOAD_FOLDER'], secure_filename(f.filename))) return render_template('upload.html') else: return render_template('index.html')@app.route('/download/<filename>', methods=['GET', 'POST'])def download(filename): # as_attachment=True 表示文件作为附件下载 return send_from_directory('./upload', filename, as_attachment=True)if __name__ == '__main__': app.run(debug=True)
- 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
在upload
函数中,f.save
的作用是将文件保存到指定路径中。在使用secure_filename
检验上传文件名称时,注意名称尽量为英文!
在download
函数中,send_from_directory
方法将upload目录下的指定文件发送到客户端,as_attachment=True
表示文件作为附件下载。
在templates文件夹下创建index.html文件,代码如下:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>实例2.上传下载文件</title></head><body> <h3>上传文件</h3> <form action="http://localhost:5000/upload" method="POST" enctype="multipart/form-data"> <input type="file" name="file" /> <input type="submit" value="提交" /> </form> <h3>下载文件</h3> <a href="/download/download_sample.txt">download_sample.txt</a></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
在templates文件夹下创建upload.html文件,代码如下:
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>实例2.文件上传成功页面</title></head><body> <h3>文件上传成功!</h3></body></html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10