虚拟环境安装

virtualenv

  • 作用

    virtualenv是一个虚拟的Python环境构建器。 它可以帮助用户并行创建多个Python环境。 因此,它可以避免不同版本的库之间的兼容性问题。

  • 安装

    1
    2
    3
    4
    5
    // pip 安装
    pip install virtualenv

    // linux安装
    sudo apt-get install virtualenv
  • 创建虚拟环境

    1
    virtualenv venv
  • 激活相应的环境

    1
    2
    3
    4
    5
    // linux
    venv/bin/activate

    //windows
    venv\scripts\activate

Flask-Swagger (生成API 文档)

Flask-Swagger 是一个用于集成 Swagger 和 Flask 的插件。它可以自动根据 Flask 应用程序中的路由信息生成 Swagger/OpenAPI 3.0 文档。

首先,我们需要安装 Flask-Swagger。可以使用 pip 命令来安装:

1
pip install flask-swagger

安装完成后,在 Flask 应用程序中导入 Flask-Swagger:

1
from flask_swagger import Swagger

接下来,创建一个 Flask 应用程序实例,并初始化 Swagger:

1
2
app = Flask(__name__)
swagger = Swagger(app)

现在,我们可以使用 @app.route 装饰器定义 API 路由,并添加 Swagger 注释。下面是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@app.route('/users', methods=['GET'])
# Swagger 注释
# @swagger
def get_users():
"""
获取用户列表

This is a sample request.

---
responses:
200:
description: 成功获取用户列表
"""

# 实现获取用户列表的逻辑
pass

在该示例代码中,@app.route 装饰器用于定义路由和请求方法。@swagger 注释用于告诉 Flask-Swagger 该路由需要生成 Swagger 文档。通过浏览器访问 /apidocs 路径即可打开 Swagger UI

Flask

  • Flask 框架 轻量级,三方支持全

  • -Django 原生组件全

环境安装

1
$ pip install Flask

使用并创建应用

1
2
3
4
5
6
7
8
9
10
from flask import Flask
# 创建 app 应用实例
app = Flask(__name__)

@app.route('/')
def hello_world():
return 'Hello World' # HttpResponse

if __name__ == '__main__':
app.run()
  • Flask构造函数将当前模块的名称(name)作为参数。

  • Flask类的route()函数是一个装饰器,它告诉应用程序哪个URL应该调用相关的函数。

    1
    2
    3
    4
    5
    6
    7
    8
    '''
    rule 参数表示与该函数绑定的URL。
    options 是要转发给底层Rule对象的参数列表。
    '''
    @app.route(rule, options)
    def fn():
    pass

  • Flask类的run()方法在本地开发服务器上运行应用程序。

    1
    2
    3
    4
    5
    from flask import Flask
    app = Flask(__name__)
    #方法中的所有参数都是可选的,作用如下表描述说明
    if __name__ == '__main__':
    app.run(host, port, debug, options)
    编号 参数 描述
    1 host 监听的主机名。默认为127.0.0.1(localhost)。 设置为’0.0.0.0’使服务器在外部可用
    2 port 监听端口号,默认为:5000
    3 debug 默认为:false。 如果设置为:true,则提供调试信息
    4 options 被转发到底层的Werkzeug服务器。

调试模式

​ flask 中的调试模式类似于前端的热更新,在开发过程中,修改代码,会自动重新启动server,重新加载最新代码,还可以提供一个有用的调试器来跟踪应用程序中的错误

​ 在运行或将调试参数传递给run()方法之前,通过将应用程序对象的调试属性设置为True来启用调试模式。

1
2
3
4
5
6
7
8
from flask import Flask
app = Flask(__name__)

if __name__ == '__main__':
app.debug = True
app.run()
# 或
app.run(debug = True)

Flask 路由

  • Flask中的route()装饰器用于将URL绑定到函数。 例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    from flask import Flask
    app = Flask(__name__)

    @app.route('/hello')
    def hello_world():
    return 'hello world'

    if __name__ == '__main__':
    app.debug = True
    app.run()

    此时浏览器应该访问 http://localhost:5000/hello

  • 也可以使用add_url_rule函数来添加一个URL规则。例如

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    from flask import Flask
    app = Flask(__name__)

    def hello_world():
    return 'hello world'
    app.add_url_rule('/hello', 'hello', hello_world)
    '''
    1、'/hello':这是要添加的URL路径,表示用户在浏览器中访问的地址。
    2、'hello':这是规则的命名,用于在代码中引用该规# 获取'/hello'路径的URL
    with app.test_request_context():
    print(url_for('hello')) # 输出:/hello则。 可以在后续代码中使用这个命名获取相应信息
    3、hello_world:这是一个函数,它将在用户访问'/hello'路径时被调用,执行相应的操作。
    '''
    if __name__ == '__main__':
    app.debug = True
    app.run()

Flask变量规则 ( 路由传参)

​ 可以通过将可变部分添加到规则参数来动态构建URL。 它作为关键字参数传递给规则所关联的函数。

1
2
3
4
5
6
7
8
9
10
from flask import Flask
app = Flask(__name__)
@app.route('/hello/<name>')
def hello_name(name):
return 'Hello %s!' % name
# 其他格式化输出:
# return "Hello {}!".format(name)
# return f"Hello {name}!"
if __name__ == '__main__':
app.run(debug = True)

​ 除了默认的字符串变量部分之外,还可以使用以下转换器构造规则 -

编号 转换器 描述
1 int 接受整数
2 float 对于浮点值
3 path 接受用作目录分隔符的斜杠符(/)

重定向 - redirect()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15


#### URL构建 - redirect重定向

* redirect重定向

​ 重定向redirect()函数。调用时,它会返回一个响应对象,并将用户重定向到具有指定状态码的另一个目标位置。

​~~~ python
Flask.redirect(location, statuscode, response)
'''
location 参数是响应应该被重定向的URL。
statuscode 参数发送到浏览器的头标,默认为302。
response 参数用于实例化响应。
'''

url_for()

url_for()函数可以动态构建特定函数,该函数接受函数的名称作为第一个参数,并接受一个或多个关键字参数,每个参数对应于URL的变量部分。

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, redirect, url_for
app = Flask(__name__)
@app.route('/admin')
def hello_admin():
return 'Hello Admin'

@app.route('/guest/<guest>')
def hello_guest(guest):
return 'Hello %s as Guest' % guest

# 利用user函数,根据name的值,跳转到不同的页面
@app.route('/user/<name>')
def user(name):
if name =='admin':
# url_for('hello_admin')表示跳转到hello_admin函数,只能被路由生命的函数
return redirect(url_for('hello_admin'))
else:
# url_for('hello_guest',guest = name)表示跳转到hello_guest函数,并将name作为新值参数传递给guest
return redirect(url_for('hello_guest',guest = name))


if __name__ == '__main__':
app.run(debug = True)
  • Flask.abort (code)

    Flask类具有带有错误代码的abort()函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    Flask.abort(code)
    '''
    code参数使用以下值之一 -
    400 - 对于错误的请求
    401 - 用于未经身份验证
    403 - 禁止
    404 - 未找到
    406 - 不可接受
    415 - 用于不支持的媒体类型
    429 - 请求过多
    '''
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, redirect, url_for, render_template, request, abort
app = Flask(__name__)
@app.route('/')
def index():
return render_template('log_in.html')

@app.route('/login',methods = ['POST', 'GET'])
def login():
if request.method == 'POST':
if request.form['username'] == 'admin' :
return redirect(url_for('success'))
else:
# 将其替换为中止(401)的调用
abort(401)
else:
return redirect(url_for('index'))

@app.route('/success')
def success():
return 'logged in successfully'

if __name__ == '__main__':
app.run(debug = True)

Flask 特殊封装

send_file()

​ 打开并返回文件内容,自动识别文件类型 Content-Type: text/plan

1
2
3
4
5
6
7
8
9
10
11
from flask import Flask
# 创建 app 应用实例
app = Flask(__name__)

@app.route('/page')
def getPage():

return send_file("./logo.png")

if __name__ == '__main__':
app.run()
jsonify()

​ 将字典变成标准的字符串 Content-Type: application/json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
from flask import Flask
# 创建 app 应用实例
app = Flask(__name__)

@app.route('/login')
def login():
user = {
"username": "fafafa",
"password": "123456"
}
return jsonify(user)

if __name__ == '__main__':
app.run()

request 实例方法

request.method - 请求方式
request.args - 请求url 中的请求参数
1
2
3
?name='123'&id='123' 
request.args.get() 获取参数
request.args.todict() 将获取数据转化为字典
request.from - FromData 数据 post请求提交
request.json - 请求头中带有 Content-Type:application/json
request.data - 请求头中不带有 From 或 FromData 会保存请求体中的原始信息
request.url - 请求路径
request.path - 请求方式
request.host - 请求 IP
request.host_url - 请求协议
request.files - 请求携带的文件信息

HTTP方法 - 请求允许,获取请求参数

编号 方法 描述
1 GET 将数据以未加密的形式发送到服务器,这最常用的方法。
2 HEAD 与GET相同,但没有响应主体
3 POST 用于将HTML表单数据发送到服务器。通过POST方法接收的数据不会被服务器缓存。
4 PUT 用上传的内容替换目标资源的所有当前表示。
5 DELETE 删除由URL给出的所有目标资源的所有表示

默认情况下,Flask路由响应GET请求。 但是,可以通过为route()装饰器提供方法参数来更改此首选项。

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, request, redirect, url_for

app = Flask(__name__)

@app.route('/success/<name>')
def success(name):
return 'welcome %s' % name

# 使用method参数,允许POST、GET、PUT、DELETE请求
@app.route('/login', methods=['POST', 'GET', 'PUT', 'DELETE'])
def login():
if request.method == 'POST':
# 通过 request.form.get('name') 获取post请求表单中的name字段
user = request.form.get('name')
return redirect(url_for('success', name=user))
elif request.method == 'GET':
# 通过 request.args.get('name') 获取get请求url中的name字段
user = request.args.get('name')
return redirect(url_for('success', name=user))
elif request.method == 'PUT':
# 通过 request.get_json() 获取put请求中的json数据
data = request.get_json()
user = data.get('name')
return redirect(url_for('success', name=user))
elif request.method == 'DELETE':
# 通过 request.form.get('name') 获取delete请求表单中的name字段
user = request.form.get('name')
return redirect(url_for('success', name=user))

if __name__ == '__main__':
app.run(debug=True)

Flask 模板

返回html模板

​ Flask可以以HTML形式返回绑定到某个URL的函数的输出。

1
2
3
4
5
6
7
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return "<h1>Hello World</h1>"
if __name__ == '__main__':
app.run(debug = True)

image-20240710231046160

使用 render_template() 返回一个html文件

​ 从Python代码生成HTML内容非常麻烦,尤其是在需要放置可变数据和Python语言元素(如条件或循环)时。经常需要转义HTML代码。

​ Flask使用jinga2模板引擎,可以利用Jinja2模板引擎技术,而不需要从函数返回硬编码HTML。

​ 可以通过render_template()函数渲染HTML文件。

​ Flask将尝试在该脚本所在的同一文件夹中查找templates文件夹中的HTML文件,其中可以动态插入变量数据。 Web模板系统由模板引擎,某种数据源和模板处理器组成。

!!! 需要创建一个 templates 文件夹;templates 文件夹为根目录相对于render_template()

1
2
3
4
5
6
7
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/hello/<user>')
def hello_name(user):
return render_template('hello.html', name = user)
if __name__ == '__main__':
app.run(debug = True)
1
2
3
4
5
6
7
8
9
10
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Flask HTTP请求方法处理</title>
</head>
<body>
<!-- Web模板包含用于变量和表达式(这些情况下为Python表达式)的HTML语法散布占位符,这些变量和表达式在模板呈现时被替换为值。 -->
<h1>Hello {{ name }}!</h1>
</body>
</html>
Jinja2模板引擎语法

​ Jinja2模板引擎使用以下分隔符来从HTML转义。

  • {% ... %} 用于多行语句
  • {{ ... }} 用于将表达式打印输出到模板
  • `` 用于未包含在模板输出中的注释
  • #... ## 用于单行语句

在html模板中使用语法

  • if… else…endif
1
2
3
4
5
6
7
8
9
10
from flask import Flask, render_template
app = Flask(__name__)

@app.route('/hello/<int:score>')
def hello_name(score):
# 将score 参数 命名marks参数 传递给hello.html
return render_template('hello.html', marks = score)

if __name__ == '__main__':
app.run(debug = True)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!-- ./templates/hello.html -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Flask模板示例</title>
</head>
<body>
{% if marks>50 %}
<h1> 通过考试!</h1>
{% else %}
<h1>未通过考试!</h1>
{% endif %}
</body>
</html>
  • for循环
1
2
3
4
5
6
7
8
9
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/result')
def result():
dict = {'phy':59,'che':60,'maths':90}
# 将dict 参数 命名result参数 传递给result.html
return render_template('result.html', result = dict)
if __name__ == '__main__':
app.run(debug = True)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<!-- ./templates/result.html -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Flask模板示例</title>
</head>
<body>
<table border = 1>
{% for key, value in result.items() %}
<tr>
<th> {{ key }} </th>
<td> {{ value }} </td>
</tr>
{% endfor %}
</table>
</body>
</html>

静态文件

​ Web应用程序通常需要一个静态文件,例如支持显示网页的JavaScript文件或CSS文件。它将在应用程序的/static上提供

1
2
3
4
5
6
7
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
return render_template("index.html")
if __name__ == '__main__':
app.run(debug = True)
1
2
3
4
5
6
7
8
9
10
<!-- ./templates/index.html -->
<html>
<head>
<!-- 引入 ./static/hello.js -->
<script type = "text/javascript" src = "{{ url_for('static', filename = 'hello.js') }}" ></script>
</head>
<body>
<input type = "button" onclick = "sayHello()" value = "Say Hello" />
</body>
</html>
1
2
3
4
// ./static/hello.js
function sayHello() {
alert("Hello World")
}

请求对象

当处理请求时,可以使用 Flask 的 request 对象来访问客户端发送的数据。以下是对 request 对象中常用属性的详细说明,并说明如何设置和获取这些属性以及需要注意的事项:

  1. form 属性:
    • 设置:在处理包含表单数据的 POST 请求时,可以通过 HTML 表单提交数据来设置此属性。
    • 获取:使用 request.form['key'] 来获取特定表单字段的值。
    • 注意事项:确保在访问 form 属性之前检查请求方法是否为 POST,以免意外访问到无效数据。
  2. args 属性:
    • 设置:直接在 URL 中添加查询参数来设置此属性。
    • 获取:使用 request.args.get('key') 来获取特定查询参数的值。
    • 注意事项:要小心处理从 URL 中获取的用户输入数据,以防止恶意输入或不安全字符。
  3. cookies 属性:
    • 设置:通过响应对象中设置 Set-Cookie 头部信息来设置此属性。
    • 获取:使用 request.cookies.get('key') 来获取特定 Cookie 的值。
    • 注意事项: 要谨慎处理敏感信息,并考虑安全性和隐私问题。
  4. file 属性: - 设置: 在 HTML 表单中添加文件上传控件,并将文件上传到服务器时,会自动填充该字段 - 获取: 使用 request.files[‘file_key’] 来访问上传文件对象 - 注意事项: 需要确保对于接收和存储上传文件做好必要的安全验证和限制
  5. method 属性: - 获取当前请求方法(GET、POST等), - 不需要手动设置此属性

以上就是对 Flask 请求对象中常用属性及其操作方式、注意事项进行了详细介绍。在实际编码过程中,请根据具体场景谨慎操作并遵循最佳实践以确保代码安全可靠。

表单处理

​ 通过post方法提交from表单数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route('/')
def student():
return render_template('student.html')

@app.route('/result',methods = ['POST', 'GET'])
def result():
if request.method == 'POST':
_result = request.form
return render_template("result.html",result = _result)
elif request.method == 'GET':
_result = None
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
<!-- 表格提交 ./template/index.html  -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Flask示例</title>
</head>
<body>
<form action = "http://localhost:5000/result" method = "POST">
<p>姓名 <input type = "text" name = "Name" /></p>
<p>物理分数: <input type = "text" name = "Physics" /></p>
<p>化学分数: <input type = "text" name = "Chemistry" /></p>
<p>数学分数: <input type ="text" name = "Mathematics" /></p>
<p><input type = "submit" value = "提交" /></p>
</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 提交表格后的信息展示 ./template/result.html -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Flask示例</title>
</head>
<body>
{% if marks==None %}
<h1> 还没有提交数据</h1>
<div><a href="http://localhost:5000/">填写数据</a></div>
{% else %}
<table border = 1>
{% for key, value in result.items() %}
<tr>
<th> {{ key }} </th>
<td> {{ value }} </td>
</tr>
{% endfor %}
</table>
{% endif %}
</body>
</html>

​ !!! 通过上述表单处理方式有个问题就是提交后的数据没办法保留;可以通过cookie或者session的方式保存信息或者当前会话;

​ Cookie以文本文件的形式存储在客户端计算机上。 其目的是记住和跟踪与客户使用有关的数据,以获得更好的访问体验和网站统计。

​ Request对象包含一个cookie的属性。 它是所有cookie变量及其对应值的字典对象,客户端已发送。 除此之外,cookie还会存储其到期时间,路径和站点的域名。

​ 在Flask中,cookies设置在响应对象上。 使用make_response()函数从视图函数的返回值中获取响应对象。 之后,使用响应对象的set_cookie()函数来存储cookie。

​ 重读cookie很容易。 可以使用request.cookies属性的get()方法来读取cookie。

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
# server
from flask import Flask, render_template, request, make_response

app = Flask(__name__)

# 通过 '/' 获取表格成绩填写
@app.route('/')
def student():
return render_template('index.html')

# post 方法用来提交表格
# get 方法用来展示提交后的表格信息
@app.route('/result', methods=['POST', 'GET'])
def result():
if request.method == 'POST':
_result = dict(request.form) # 将 ImmutableMultiDict 转换为字典,避免后面读取cookie数据失败
resp = make_response(render_template("result.html", result=_result))
# 添加cookie信息, 和cookie过期时间单位ms
resp.set_cookie('result', str(_result), max_age=3600) # 有效期1小时
return resp
elif request.method == 'GET':
_result = request.cookies.get('result')
if _result:
_result = eval(_result) # 将字符串转换回字典
print(_result)
return render_template("result.html", result=_result)
else:
return render_template("result.html", result=None)

if __name__ == '__main__':
app.run(debug=True)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 表格提交 ./template/index.html  -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Flask示例</title>
</head>
<body>
<form action = "http://localhost:5000/result" method = "POST">
<p>姓名 <input type = "text" name = "Name" /></p>
<p>物理分数: <input type = "text" name = "Physics" /></p>
<p>化学分数: <input type = "text" name = "Chemistry" /></p>
<p>数学分数: <input type ="text" name = "Mathematics" /></p>
<p><input type = "submit" value = "提交" /></p>
</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 提交表格后的信息展示 ./template/result.html -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Flask示例</title>
</head>
<body>
{% if marks==None %}
<h1> 还没有提交数据</h1>
<div><a href="http://localhost:5000/">填写数据</a></div>
{% else %}
<table border = 1>
{% for key, value in result.items() %}
<tr>
<th> {{ key }} </th>
<td> {{ value }} </td>
</tr>
{% endfor %}
</table>
{% endif %}
</body>
</html>

使用set_cookie 将存储在cookie的信息一同发给用户,记录在浏览器端 ( 避免敏感信息)

image-20240711013344347

后续在请求中就会在请求中带上这上Cookie

image-20240711013843475

当设置过期时间时,会将有效时间保存在浏览器端;cookie 的过期时间是由客户端(浏览器)管理的,当客户端发送包含过期 cookie 的请求时,服务器会像处理未过期的 cookie 一样处理它

image-20240711014403422

Session 处理

​ Flask 可以通过 app.secret_key 给当前 app server 添加session会话id,用来保存当前会话的信息

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
# server
from flask import Flask, render_template, request, session
app = Flask(__name__)
# 添加session会话的id
app.secret_key = '123123'

# 通过 '/' 获取表格成绩填写
@app.route('/')
def student():
return render_template('index.html')

# post 方法用来提交表格
# get 方法用来展示提交后的表格信息
@app.route('/result', methods=['POST', 'GET'])
def result():
try:
_result_len = len(session['result'])
except:
_result_len = 0

if request.method == 'POST':
_result = request.form
# 当提交表单是会向session设置'result'字段,同时向浏览器
session['result'] = _result
return render_template("result.html", result=session['result'])
elif request.method == 'GET' and _result_len > 0:
return render_template("result.html", result=session['result'])
elif request.method == 'GET' and _result_len == 0:
return render_template("result.html", result=None)

if __name__ == '__main__':
app.run(debug=True)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!-- 表格提交 ./template/index.html  -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Flask示例</title>
</head>
<body>
<form action = "http://localhost:5000/result" method = "POST">
<p>姓名 <input type = "text" name = "Name" /></p>
<p>物理分数: <input type = "text" name = "Physics" /></p>
<p>化学分数: <input type = "text" name = "Chemistry" /></p>
<p>数学分数: <input type ="text" name = "Mathematics" /></p>
<p><input type = "submit" value = "提交" /></p>
</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<!-- 提交表格后的信息展示 ./template/result.html -->
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Flask示例</title>
</head>
<body>
{% if marks==None %}
<h1> 还没有提交数据</h1>
<div><a href="http://localhost:5000/">填写数据</a></div>
{% else %}
<table border = 1>
{% for key, value in result.items() %}
<tr>
<th> {{ key }} </th>
<td> {{ value }} </td>
</tr>
{% endfor %}
</table>
{% endif %}
</body>
</html>

当提交表单是会向session设置’result’字段,同时向浏览器Cookie中记录当前session

image-20240711012049022

后续在请求中就会在请求头中带上这个Set-Cookie

image-20240711012113348

  • 添加会话变量

    1
    session['username'] = 'admin'
  • 删除会话变量

    1
    session.pop('username', None)

Token 处理

pass

消息闪现 - 对话框或消息框 >>>

​ 一个基于GUI好的应用程序需要向用户提供交互的反馈信息。 例如,桌面应用程序使用对话框或消息框,JavaScript使用alert()函数用于类似的目的;

​ Flask框架的闪现系统使得可以在一个视图中创建一个消息并将其呈现在名为next的视图函数中。

  • flash()

    Flask模块包含flash()方法。 它将消息传递给下一个请求,该请求通常是一个模板。

    1
    2
    3
    4
    5
    flash(message, category)
    '''
    message - 参数是要刷新的实际消息。
    category - 参数是可选的。 它可以是’错误’,’信息’或’警告’。
    '''
  • get_flashed_messages()

    要从会话中删除消息,模板调用get_flashed_messages()函数。

    1
    2
    3
    4
    5
    6
    get_flashed_messages(with_categories, category_filter)
    '''
    两个参数都是可选的
    第一个参数是元组
    第二个参数对于仅显示特定消息有用。
    '''

文件上传

​ Flask中处理文件上传非常简单。 它需要一个enctype属性设置为’multipart/form-data’的HTML表单,将该文提交到指定URL。 URL处理程序从request.files[]对象中提取文件并将其保存到所需的位置。

​ 每个上传的文件首先保存在服务器上的临时位置,然后再保存到最终位置。 目标文件的名称可以是硬编码的,也可以从request.files [file]对象的filename属性中获取。 但是,建议使用secure_filename()函数获取它的安全版本。

​ Flask对象的配置设置中定义默认上传文件夹的路径和上传文件的最大大小。

变量 说明
app.config[‘UPLOAD_FOLDER’] 定义上传文件夹的路径
app.config[‘MAX_CONTENT_PATH’] 指定要上传的文件的最大大小 - 以字节为单位
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from flask import Flask, render_template, request
from werkzeug import secure_filename
app = Flask(__name__)

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
if request.method == 'POST':
f = request.files['file']
print(request.files)
保存文件
f.save(secure_filename(f.filename))
return 'file uploaded successfully'
else:
return render_template('upload.html')

if __name__ == '__main__':
app.run(debug = True)

SQLite

​ 其特点为:

  • 零配置,无需安装和管理配置;
  • 储存在单一磁盘文件中的一个完整的数据库;
  • 数据库文件可以在不同字节顺序的机器间自由的共享;
  • 足够小, 大致13万行C代码, 4.43M,支持数据库大小可至2TB;
  • 数据库操作快;
  • 不需要任何外部的依赖。
存储类 描述
NULL 值是一个 NULL 值。
INTEGER 值是一个带符号的整数,根据值的大小存储在 1、2、3、4、6 或 8 字节中。
REAL 值是一个浮点值,存储为 8 字节的 IEEE 浮点数字。
TEXT 值是一个文本字符串,使用数据库编码(UTF-8、UTF-16BE 或 UTF-16LE)存储。
BLOB 值是一个 blob 数据,完全根据它的输入存储
  • 创建一个SQLite数据库
    创建一个SQLite数据库 ‘database.db’并在其中创建一个student表。

    1
    2
    3
    4
    5
    6
    7
    8
    import sqlite3
    # 创建连接SQLite数据库 *‘database.db’*
    conn = sqlite3.connect('database.db')
    print("Opened database successfully");
    # 创建一个student表,并输入字段与字段类型 name TEXT, addr TEXT, city TEXT, pin TEXT
    conn.execute('CREATE TABLE students (name TEXT, addr TEXT, city TEXT, pin TEXT)') #执行单条sql语句
    print("Table created successfully");
    conn.close()
  • 案例 - 通过html向flask后端服务存储SQlite 学生数据

    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
    45
    46
    47
    48
    49
    from flask import Flask, render_template, request
    from werkzeug import secure_filename
    # 创建实例
    app = Flask(__name__)

    # 访问主页面返回index.html
    @app.route('/', methods=['GET'])
    def new_student():
    return render_template('index.html')

    # 访问 '/create' 渲染添加学生信息模板
    @app.route('/create')
    def create_student():
    return render_template('addStudent.html') #渲染addStudent.html模板

    # 添加到数据库中
    @app.route('/addStudent', methods = ['POST'])
    def add_student():
    try:
    #获取请求中的数据
    nm = request.form['name']
    addr = request.form['address']
    city = request.form['city']
    pin = request.form['postcode']
    with sqlite3.connect("database.db") as con: #建立与database.db数据库的连接
    cur = con.cursor() #获取游标
    cur.execute("INSERT INTO students (name,addr,city,pin) VALUES (?,?,?,?)",(nm,addr,city,pin) ) #添加数据,执行单条的sql语句
    con.commit() #提交事务
    msg = "数据添加成功"
    except:
    con.rollback() #撤消当前事务中所做的所有更改
    msg = "操作失败"
    finally:
    return render_template("result.html",msg = msg) #渲染result.html模板并传递msg值
    con.close() #关闭数据库连接

    # 查询数据库数据
    @app.route('/showData')
    def show_student():
    con = sqlite3.connect("database.db") #建立数据库连接
    con.row_factory = sqlite3.Row #设置row_factory,对查询到的数据,通过字段名获取列数据
    cur = con.cursor() #获取游标
    cur.execute("select * from students") #执行sql语句选择数据表
    rows = cur.fetchall() #获取多条记录数据
    return render_template("studentList.html",rows = rows) #渲染show.html模板并传递rows值


    if __name__ == '__main__':
    app.run(debug = True)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    <!-- index.html -->
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>学生系统</title>
    </head>
    <body>
    <div>
    <a href='/create'>添加学生信息</a>
    <span> | </span>
    <a href='/studentList'>学生信息列表</a>
    </div>
    </body>
    </html>
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    <!-- addStudent.html -->
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Title</title>
    </head>
    <body>
    <!-- 向add_student函数传递from表单数据 -->
    <form action = "{{ url_for('add_student') }}" method = "POST">
    <p>姓名<input type="text" name="name" /></p>
    <p>地址<textarea name="address" ></textarea></p>
    <p>城市<input type="text" name="city" /></p>
    <p>邮编<input type="text" name="postcode" /></p>
    <input type="submit" value="提交" />
    </form>
    </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
    33
    34
    <!-- studentList.html -->
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Flask示例</title>
    <style type="text/css">
    body {background-color: #ffffcc;}
    div {width: 500px; margin: 0 auto;text-align: center;}
    table {margin: 0 auto;}
    td {border:1px solid black;}
    </style>
    </style>
    </head>
    <body>
    <div>
    <thead>
    <td>改名</td>
    <td>地址</td>
    <td>城市</td>
    <td>编码</td>
    </thead>
    {% for row in rows %}
    <tr>
    <td>{{row["name"]}}</td>
    <td>{{row["addr"]}}</td>
    <td>{{row["city"]}}</td>
    <td>{{row['pin']}}</td>
    </tr>
    {% endfor %}
    </table>
    <div><a href = "/">返回主页</a></div>
    </div>
    </body>
    </html>

扩展 - Flask-SQLAlchemy

ORM(对象关系映射)

​ RDBMS服务器中的数据以表格形式存储。 对象关系映射是一种将对象参数映射到底层RDBMS表结构的技术。 ORM API提供了执行CRUD操作的方法,而无需编写原始SQL语句。

  • 安装

    1
    2
    pip install flask-sqlalchemy
    pip install pymysql
  • 从该模块导入SQLAlchemy类。

    1
    from flask_sqlalchemy import SQLAlchemy
  • 创建一个Flask应用程序对象并为要使用的数据库设置URI。

    1
    2
    app = Flask(__name__)
    app.config['DATABASE_URL'] = 'mysql://root:root@127.0.0.1/blog'
  • 然后用应用程序对象作为参数创建一个SQLAlchemy类的对象。 该对象包含ORM操作的辅助函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    db = SQLAlchemy(app)

    class students(db.Model):
    # 定义这个类使用的数据库
    id = db.Column('student_id', db.Integer, primary_key = True)
    # 定义字段
    name = db.Column(db.String(100))
    city = db.Column(db.String(50))
    addr = db.Column(db.String(200))
    pin = db.Column(db.String(10))

    # 初始化实例赋值
    def __init__(self, name, city, addr,pin):
    self.name = name
    self.city = city
    self.addr = addr
    self.pin = pin
  • 创建/使用URI中提到的数据库,请运行create_all()方法

    创建数据库中所有尚未在数据库中存在的表。这个函数通常在开发过程中使用,特别是当你添加、修改了模型类后需要将这些变化同步到数据库时。

    1
    2
    3
    4
    5
    # 创建所有尚未在数据库中存在的表
    db.create_all()
    '''
    请注意,在生产环境中并不推荐使用 create_all() 方法来自动创建表格。相反,建议使用迁移工具来管理和更新数据模型。
    '''
  • 使用SQLAlchemy的Session对象管理ORM对象的所有持久性操作

    • 增 - db.session.add(模型对象) - 将一条记录插入到映射表中

      1
      2
      3
      4
      # 假设我们有一个名为User的模型
      new_user = User(username='john_doe', email='john.doe@example.com')
      db.session.add(new_user) # 将新用户对象添加到会话中
      db.session.commit() # 提交更改到数据库
    • 删 - db.session.delete(模型对象) - 从表中删除记录

      1
      2
      3
      4
      # 假设我们有一个名为User的模型
      user = User.query.get(1) # 获取ID为1的用户
      db.session.delete(user) # 删除用户记录
      db.session.commit() # 提交更改到数据库
    • 查 - model.query.all() - 从表中检索所有记录(对应于SELECT查询)。

      1
      2
      3
      @app.route('/')
      def show_all():
      return render_template('show_all.html', students = students.query.all() )
    • 更改 or 查单个数据 - 可以使用filter属性将筛选器应用于检索到的记录集。

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      # 获取单个数据
      Students.query.filter_by(city = 'Haikou').all()


      # 更新数据
      # 假设我们有一个名为User的模型,并且存在一个用户名为'john_doe'的用户
      user = User.query.filter_by(username='john_doe').first()
      if user:
      user.email = 'new_email@example.com' # 修改email属性值
      db.session.commit() # 提交更改到数据库

驱动包
  • MySQL-python:也就是MySQLdb。是对C语言操作 MySQL数据库的一个简单封装。遵循了Python-DB-API v2。但是只支持Python2。
  • mysqlclient:是 MySQL-python的另外一个分支。支持Python3并且修复了一些bug。是目前为止执行效率最高的驱动,但是安装的时候容易因为环境问题出错。
  • pymysql:纯 Python实现的一个驱动。因为是纯 Python编写的,因此执行效率不如 mysqlclient。也正因为是纯 Python写的,因此可以和 Python代码无缝衔接。
  • mysql-connector-python: MySQL官方推出的纯 Python连接MySQL的驱动,执行效率pymysql还慢。

扩展 - Flask-restful

Flask-RESTful 是一个 Flask 扩展,它添加了快速构建 REST APIs 的支持。它当然也是一个能够跟你现有的ORM/库协同工作的轻量级的扩展。

RESTful 风格 设计介绍

REST 系统的特点:

  • 客户端-服务器: 客户端和服务器之间隔离,服务器提供服务,客户端进行消费。
  • 无状态: 从客户端到服务器的每个请求都必须包含理解请求所必需的信息。换句话说, 服务器不会存储客户端上一次请求的信息用来给下一次使用。
  • 可缓存: 服务器必须明示客户端请求能否缓存。
  • 分层系统: 客户端和服务器之间的通信应该以一种标准的方式,就是中间层代替服务器做出响应的时候,客户端不需要做任何变动。
  • 统一的接口: 服务器和客户端的通信方法必须是统一的。
  • 按需编码: 服务器可以提供可执行代码或脚本,为客户端在它们的环境中执行。这个约束是唯一一个是可选的。
1
2
3
4
5
6
7
8
9
10
HTTP 标准的方法有如下:
========== ===================== ==================================
HTTP 方法 行为 示例
========== ===================== ==================================
GET 获取资源的信息 http://example.com/api/orders
GET 获取某个特定资源的信息 http://example.com/api/orders/123
POST 创建新资源 http://example.com/api/orders
PUT 更新资源 http://example.com/api/orders/123
DELETE 删除资源 http://example.com/api/orders/123
========== ====================== ==================================
安装
1
2
3
4
5
pip install flask-restful
# 开发的版本可以从 GitHub 上的页面 下载
git clone https://github.com/twilio/flask-restful.git
cd flask-restful
python setup.py develop
创建请求方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from flask import Flask  
from flask_restful import Api, Resource

# 创建Flask应用
app = Flask(__name__)

# 创建API对象
api = Api(app)

# 创建一个资源类,用于处理请求
class HelloWorld(Resource):
def get(self):
return {'hello': 'world'}

# 将资源类添加到API,并指定路由
api.add_resource(HelloWorld, '/')

# 程序入口:运行Flask应用
if __name__ == '__main__':
app.run(debug=True)
'''
测试:
$ curl http://localhost:5000
'''
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
from flask import Flask, request  
from flask_restful import Api, Resource

# 创建Flask应用
app = Flask(__name__)

# 创建API对象
api = Api(app)

# todos字典,用于存储待办事项数据
todos = {'1':'name'}

# 创建一个资源类,用于处理对特定待办事项的请求
class TodoSimple(Resource):
# 处理GET请求的方法,返回指定todo_id的数据
def get(self, todo_id):
return {todo_id: todos[todo_id]}

# 处理PUT请求的方法,将提交的数据存入todos字典中,并返回更新后的数据
def put(self, todo_id):
# 将提交的数据存入todos字典中
todos[todo_id] = request.form['data']
return {todo_id: todos[todo_id]}

# 将TodoSimple资源类与路由绑定,并使用<..>定义URL参数(todo_id)
api.add_resource(TodoSimple, '/<string:todo_id>')

# 程序入口:运行Flask应用,启用调试模式
if __name__ == '__main__':
app.run(debug=True)

'''
测试:
get请求:
$ curl http://localhost:5000/todo1
put添加:
$ curl http://localhost:5000/todo1 -d "data=Remember the milk" -X PUT
'''
设置多个路由端点
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
from flask import Flask
from flask_restful import Api, Resource

# 创建Flask应用
app = Flask(__name__)

# 创建API对象
api = Api(app)


# 创建一个资源类,用于处理请求
class HelloWorld1(Resource):
def get(self):
return {'hello': 'world1'}


class HelloWorld2(Resource):
def get(self):
return {'hello': 'world2'}


# 将资源类添加到API,并指定路由
api.add_resource(HelloWorld1, '/')
# 设置多个路由端点
api.add_resource(HelloWorld2,
'/hello1',
'/hello2')

# 程序入口:运行Flask应用
if __name__ == '__main__':
app.run(debug=True)
'''
测试:
get请求:
$ curl http://localhost:5000
$ curl http://localhost:5000/hello1
$ curl http://localhost:5000/hello2
'''
案例:todoList
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
from flask import Flask
from flask_restful import reqparse, abort, Api, Resource

app = Flask(__name__)
api = Api(app)
TODOS = {
'todo1': {'task': 'build an API'},
'todo2': {'task': '?????'},
'todo3': {'task': 'profit!'},
}


def abort_if_todo_doesnt_exist(todo_id):
if todo_id not in TODOS:
abort(404, message="Todo {} doesn't exist".format(todo_id))


parser = reqparse.RequestParser()
parser.add_argument('task', type=str)


## Todo
## 显示单个代办事项,并允许删除
class Todo(Resource):
def get(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
return TODOS[todo_id]

def delete(self, todo_id):
abort_if_todo_doesnt_exist(todo_id)
del TODOS[todo_id]
return '', 204

def put(self, todo_id):
args = parser.parse_args()
task = {'task': args['task']}
TODOS[todo_id] = task
return task, 201


## TodoList
## 显示所有代办列表,并允许post新添加任务
class TodoList(Resource):
def get(self):
return TODOS

def post(self):
args = parser.parse_args()
todo_id = int(max(TODOS.keys()).lstrip('todo')) + 1
todo_id = 'todo%i' % todo_id
TODOS[todo_id] = {'task': args['task']}
return TODOS[todo_id], 201


# 设置资源
api.add_resource(TodoList, '/todos')
api.add_resource(Todo, '/todo/<todo_id>')
if __name__ == '__main__':
app.run(debug=True)

'''
测试:
获取列表
$ curl http://localhost:5000/todos
获取单个事务
$ curl http://localhost:5000/todo/todo1
删除单个事务
$ curl http://localhost:5000/todos/todo2 -X DELETE -v
新增一个事务
$ curl http://localhost:5000/todos -d "task=something new" -X POST -v
更新一个事务
$ curl http://localhost:5000/todos/todo3 -d "task=something different" -X PUT -v
'''
字段类型定义 - fields & 接口类型推断 - @marshal_with()
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
45
46
47
48
49
50
# 基本用法
# 可以定义一个字典或者 fields 的 OrderedDict 类型
from flask_restful import fields, marshal_with
# 定义类型
user_fields = {
'id': fields.Integer,
'username': fields.String(default='fafafa'),
'email': fields.String(attribute='email_address'),
}

'''
Flask请求方法中使用marshal_with装饰器时,它会自动将返回值转换为指定字段格式的JSON响应,并忽略任何直接打印或返回的内容。
将该get请求数据与user_fields 定义类型进行绑定
'''
class User(Resource):
@marshal_with(user_fields) # 或者使用marshal进行数据重组
def get(self, user_id):
# 获取用户信息
user = {'id': user_id, 'email_address': 'example@example.com'}
return user
# {"id": 1,"username": "fafafa","email": "example@example.com"}
'''
String:用于表示字符串类型的字段。
Integer:用于表示整数类型的字段。
Float:用于表示浮点数类型的字段。
Boolean:用于表示布尔值类型的字段。
DateTime:用于表示日期和时间类型的字段。
Nested:允许你嵌套其他 fields 定义,以便处理复杂结构的数据。例如,你可以在一个对象中嵌套另一个对象或列表等复杂结构数据。
List:用于定义列表型数据,可以嵌套其他 fields 定义来处理列表中元素的格式和类型。
Raw:直接将原始值返回,不进行格式化处理。通常用在需要自定义处理逻辑时使用。

每种字段都有一些可选参数来定制响应输出:
Attribute: 这个参数用于指定需要序列化对象上相对应的属性名。如果不指定 attribute,则默认使用与字段同名的属性
user_fields = {
'id': fields.Integer(attribute='user_id'),
'username': fields.String,
}
Default: 当指定属性不存在时返回默认值
user_fields = {
'id': fields.Integer,
'username': fields.String,
'email': fields.String(default='example@example.com'),
}
Format: 用于格式化输出结果,在响应中将值转换为特定格式。例如,你可以使用 date_format 来将日期格式化为特定的字符串形式。
user_fields = {
'birth_date': fields.DateTime(dt_format='iso8601')
}
Required: 设置是否必须包含该属性,默认为 True。当设为 False 时,如果对象上没有该属性,则不会在响应中包含该字段。
Nullable:设置是否允许该字段的值为空(None)。默认情况下,所有字段都允许为空
'''
自定义字段类型匹配 - fields.Raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from flask_restful import fields 
# 将类继承fields,新添加自定义匹配字段
# 匹配成功时,将会转换为大写
class UrgentItem(fields.Raw):
def format(self, value):
return value.upper()
# 匹配成功时,将会转换为小写
class UnreadItem(fields.Raw):
def format(self, value):
return value.lower()

fields = {
'name': fields.String,
# 使用自定义匹配
'priority': UrgentItem(attribute='flags'),
'status': UnreadItem(attribute='flags'),
}
扁平化处理 - 结构重组( marshal)
  • 字典字段 - { }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 字典字段
from flask_restful import fields, marshal
import json
# 原字典数据 - 从数据库返回
data = {'name': 'bob', 'addr1': '123 fake street', 'addr2': '', 'city': 'New York', 'state': 'NY', 'age': '21'}

# 定义结构
resource_fields = {'name': fields.String, 'age': fields.Integer, 'address': {fields.String}}
resource_fields['address']['line 1'] = fields.String(attribute='addr1')
resource_fields['address']['line 2'] = fields.String(attribute='addr2')
resource_fields['address']['city'] = fields.String
resource_fields['address']['state'] = fields.String

# 根据定义结构进行重组,并转成json字符串
json.dumps(marshal(data, resource_fields))
'''{"name": "bob", "age": "21", "address": {"line 1": "123 fake street", "line 2": "", "state": "NY", "city": "New York"}}'''
  • 列表字段 - [ ]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 列表字段
from flask_restful import fields, marshal
import json
# 原数据
data = {'type': 'slide', 'pic_image1': '/upload/images/01.png', 'pic_image2': '/upload/images/02.png', 'pic_image3': '/upload/images/03.png'}

# 将'pic_images'字段加入到原数据中
data['pic_images'] = [data['pic_image1'], data['pic_image2'], data['piicmage_3']]
del data['piicmage_1']
del data['piicmage_2']
del data['piicmage_3']

# 定义结构
resource_fields = {'type': fields.String, 'images': fields.List(fields.String)}

# 根据定义的结构进行重组,并转成json字符串
json_data = json.dumps(marshal(data, resource_fields))
'''{"type": "slide", "pic_images": ["/upload/images/01.png", "/upload/images/02.png", "/upload/images/03.png"]}'''
  • 嵌套类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from flask_restful import fields, marshal
import json
# 里层原数据
address1 = {'addr1': '123 fake street', 'city': 'New York', 'state': 'NY', 'zip': '10468'}
address2 = {'addr1': '555 nowhere', 'city': 'New York', 'state': 'NY', 'zip': '10468'}
# 外层数据
data = {'name': 'bob', 'billing_address': address1, 'shipping_address': address2}

# 定义里层数据结构
address_fields = {'line 1': fields.String(attribute='addr1'), 'line 2': fields.String(attribute='addr2'),
'city': fields.String(attribute='city'), 'state': fields.String(attribute='state'),
'zip': fields.String(attribute='zip')}

# 定义外层数据结构 Nested-解组
resource_fields = {'name': fields.String, 'billing_address': fields.Nested(address_fields),
'shipping_address': fields.Nested(address_fields)}

json_data = json.dumps(marshal(data, resource_fields))
'''{"billing_address": {"line 1": "123 fake street", "line 2": null, "state": "NY", "zip": "10468", "city": "New York"}, "name": "bob", "shipping_address": {"line 1": "555 nowhere", "line 2": null, "state": "NY", "zip": "10468", "city": "New York"}}'''
内容协商
1
2
3
4
5
6
7
8
9
app = Flask(__name__)
api = restful.Api(app)

# 使用@api.representation() 装饰器来协商该接口的返回对象
@api.representation('application/json')
def output_json(data, code, headers=None):
resp = make_response(json.dumps(data), code)
resp.headers.extend(headers or {})
return resp

扩展 - Flask-WTF

​ 使用Flask-WTF,可以在Python脚本中定义表单域并使用HTML模板来呈现它们。 也可以将验证应用于WTF字段

  • 安装依赖

    1
    pip install flask-WTF
  • Form类,该类必须用作用户定义表单的父级,标准表单字段

    编号 标准表单字段 描述
    1 TextField 表示 HTML表单元素
    2 BooleanField 表示 HTML表单元素
    3 DecimalField 用小数显示数字的文本字段
    4 IntegerField 用于显示整数的文本字段
    5 RadioField 表示的HTML表单元素
    6 SelectField 表示选择表单元素
    7 TextAreaField 表示 html表单元素
    8 PasswordField 表示 HTML表单元素
    9 SubmitField 表示表单元素
  • 案例 - 一个包含文本字段的表单

    1
    2
    3
    4
    5
    from flask_wtf import Form
    from wtforms import TextField
    # 除了name字段之外,还会自动创建一个CSRF令牌的隐藏字段。 这是为了防止跨站请求伪造攻击。
    class ContactForm(Form):
    name = TextField("Name Of Student")

扩展 - Flask-Testing

扩展 - Flask-PyMongo

扩展 - Flask-Mail

​ 基于Web的应用程序通常需要具有向用户/客户端发送邮件的功能。 Flask-Mail扩展

  • 安装扩展

    1
    pip install Flask-Mail
  • 配置Flask-Mail

编号 参数 描述
1 MAIL_SERVER 邮件服务器的名称/IP地址
2 MAIL_PORT 所用服务器的端口号
3 MAIL_USE_TLS 启用/禁用传输安全层加密
4 MAIL_USE_SSL 启用/禁用安全套接字层加密
5 MAIL_DEBUG 调试支持,默认是Flask应用程序的调试状态
6 MAIL_USERNAME 发件人的用户名
7 MAIL_PASSWORD 发件人的密码
8 MAIL_DEFAULT_SENDER 设置默认发件人
9 MAIL_MAX_EMAILS 设置要发送的最大邮件
10 MAIL_SUPPRESS_SEND 如果app.testing设置为true,则发送被抑制
11 MAIL_ASCII_ATTACHMENTS 如果设置为true,则将附加的文件名转换为ASCII
  • flask-mail模块包含以下重要类的定义

    • Mail类

      它管理电子邮件消息的要求。 类构造函数采用以下形式

      编号 方法 描述
      1 send() 发送Message类对象的内容
      2 connect() 与邮件主机打开连接
      3 send_message() 发送消息对象
    • Message类

      它封装了一封电子邮件,Message类的构造函数有几个参数

      1
      2
      flask-mail.Message(subject, recipients, body, html, sender, cc, bcc, 
      reply-to, date, charset, extra_headers, mail_options, rcpt_options)
    • Message类方法

      • attach() - 向消息添加附件。 该方法采用以下参数
        • filename - 要附加的文件的名称
        • content_type - 文件的MIME类型
        • data - 原始文件数据
        • disposition - 内容处置
      • add_recipient() - 向消息添加另一个收件人
  • 代码示例

    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
    45
    46
    47
    # 第1步 flask-mail模块导入Mail和Message类
    from flask_mail import Mail, Message

    # 第2步 - 然后根据以下设置配置Flask-Mail。
    app.config['MAIL_SERVER']='smtp.gmail.com'
    app.config['MAIL_PORT'] = 465
    app.config['MAIL_USERNAME'] = 'yourId@gmail.com'
    app.config['MAIL_PASSWORD'] = '*****'
    app.config['MAIL_USE_TLS'] = False
    app.config['MAIL_USE_SSL'] = True

    # 第3步 - 创建一个Mail类的实例
    mail = Mail(app)

    # 第4步 - 在由URL规则映射的Python函数(‘/‘)中设置Message对象
    @app.route("/")
    def index():
    msg = Message('Hello', sender = 'yourId@gmail.com', recipients = ['id1@gmail.com'])
    msg.body = "This is the email body"
    mail.send(msg)
    return "Sent"

    # 第5步 - 整个代码如下。 在Python Shell中运行以下脚本并访问URL: http://localhost:5000/
    from flask import Flask
    from flask_mail import Mail, Message
    # 创建app实例
    app =Flask(__name__)

    # 设置mail信息
    mail=Mail(app)
    app.config['MAIL_SERVER']='smtp.gmail.com'
    app.config['MAIL_PORT'] = 465
    app.config['MAIL_USERNAME'] = 'yourId@gmail.com'
    app.config['MAIL_PASSWORD'] = '*****'
    app.config['MAIL_USE_TLS'] = False
    app.config['MAIL_USE_SSL'] = True
    mail = Mail(app)

    @app.route("/")
    def index():
    msg = Message('Hello', sender = 'yourId@gmail.com', recipients = ['id1@gmail.com'])
    msg.body = "Hello Flask message sent from Flask-Mail"
    mail.send(msg)
    return "Sent"

    if __name__ == '__main__':
    app.run(debug = True)

扩展 - Flask-Login

扩展 - Flask-Exceptional

扩展 - Flask-DebugToolbar

扩展 - Flaks-Dashed

扩展 - Flask-Celery

扩展 - Flask-Celery

扩展 - Flask-Cache

扩展 - Flask-Babel