最全的零基础Flask教程

1 Flask介绍

1.1 为什么要使用Flask

Django和Flask是Python使用最多的两个框架

1.2 Flask是什么

Flask诞生于2010年,是Armin ronacher(人名)用 Python 语言基于 Werkzeug 工具箱编写的轻量级Web开发框架。

Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login,数据库Flask-SQLAlchemy),都需要用第三方的扩展来实现。比如可以用 Flask 扩展加入ORM、窗体验证工具,文件上传、身份验证等。Flask 没有默认使用的数据库,你可以选择 MySQL,也可以用 NoSQL。

其 WSGI 工具箱采用 Werkzeug(路由模块),模板引擎则使用 Jinja2。这两个也是 Flask 框架的核心。

1.3 Flask与Django框架对比

1.3.1 框架轻重

重量级的框架:为方便业务程序的开发,提供了丰富的工具、组件,如Django

轻量级的框架:只提供Web框架的核心功能,自由、灵活、高度定制,如Flask、Tornado

1.3.2 与Django对比

django提供了:

  • django-admin快速创建项目工程目录
  • manage.py 管理项目工程
  • orm模型(数据库抽象层)
  • admin后台管理站点
  • 缓存机制
  • 文件存储系统
  • 用户认证系统
  • 而这些,flask都没有,都需要扩展包来提供

1.4 Flask常用扩展包

  • Flask-SQLalchemy:操作数据库;
  • Flask-script:插入脚本;
  • Flask-migrate:管理迁移数据库;
  • Flask-Session:Session存储方式指定;
  • Flask-WTF:表单;
  • Flask-Mail:邮件;
  • Flask-Bable:提供国际化和本地化支持,翻译;
  • Flask-Login:认证用户状态;
  • Flask-OpenID:认证;
  • Flask-RESTful:开发REST API的工具;
  • Flask-Bootstrap:集成前端Twitter Bootstrap框架;
  • Flask-Moment:本地化日期和时间;
  • Flask-Admin:简单而可扩展的管理接口的框架

1.5 Flask文档

2 工程搭建

2.1 环境安装

2.1.1 Anaconda常用虚拟环境命令

# 虚拟环境
conda create -n 虚拟环境名称 python=python版本号 # 创建虚拟环境
conda remove -n 虚拟环境名称 --all # 删除虚拟环境
conda activate 虚拟环境名称 # 激活虚拟环境
conda env list 或 conda info -e # 查看全部虚拟环境
deactivate 虚拟环境名称 # 退出虚拟环境

2.1.2. 创建虚拟环境

Flask 再2.3.0版本上已经放弃对Python3.7的支持,当前支持的Python把版本区间为3.7~3.12,因此这里将Python的版本定为3.8

conda create -n FlaskWeb python=3.8

2.1.3. 安装Flask

使用flask 2.3.0版本

pip install flask==2.3.0

2.2 HelloWorld程序

2.2.1 Flask编写

2.2.2 Flaks手动运行

1 Pycharm运行

2 命令行运行

3 访问测试

2.3 参数说明

2.3.1 Flask对象初始化参数

Flask 程序实例在创建的时候,需要默认传入当前 Flask 程序所指定的包(模块),下面说明一些常用的Flask参数:

1 import_name

  • Flask程序所在的包(模块),传 __name__ 就可以
  • 其可以决定 Flask 在访问静态文件时查找的路径

2 static_url_path

  • 静态文件访问路径,可以不传,默认为:/ + static_folder

3 static_folder

  • 静态文件存储的文件夹,可以不传,默认为 static

template_folder

  • 模板文件存储的文件夹,可以不传,默认为 templates

默认参数情况下,访问静态资源

app = Flask(__name__)

修改参数的情况下,访问静态资源

# 定义静态资源的访问路径为url_path_param,静态资源文件夹名称为folder_param
app = Flask(__name__, static_url_path='/url_path_param', static_folder='folder_param')

2.3.2 应用程序配置参数

Flask将配置信息保存到了app.config属性中,该属性可以按照字典类型进行操作。

1 读取配置参数

  • app.config.get(name)
  • app.config[name]

2 设置

Flask配置参数,主要使用以下三种方式:

从配置对象中加载

app.config.from_object(配置对象)

# 定义默认配置类
class DefultConfig(object):
"""
默认配置
"""
SECRET_KEY = "lalalalalala"
# 定义开发环境下的配置类,继承自默认配置类
class DevDefultConfig(DefultConfig):
DEBUG=True app = Flask(__name__)
# 从配置对象中添加配置
app.config.from_object(DefultConfig)

应用场景:

从配置文件中加载

app.config.from_pyfile(配置文件)

新建一个配置文件setting.py

SECRET_KEY = 'TPmi4aLWRbyVq8zu9v82dWYW1'

在Flask程序文件中读取该配置文件

# 从配置文件中添加配置
app.config.from_pyfile("setting.py")
@app.route("/")
def index():
return app.config["SECRET_KEY"]

访问测试

3 从环境变量中加载配置信息

环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等。

环境变量是在操作系统中一个具有特定名字的对象,它包含了一个或者多个应用程序所将使用到的信息。

通俗的理解,环境变量就是我们设置在操作系统中,由操作系统代为保存的变量值

Flask使用环境变量加载配置的本质是通过环境变量值找到配置文件,再读取配置文件的信息,其使用方式为:


代码查询环境变量的方式:

# 环境变量中存储的是配置文件的绝对地址
app.config.from_envvar('环境变量名')

两种定义环境变量的方式

Windows环境下定义临时环境变量

set 临时环境变量名称 = 变量的值

Pycharm下定义环境变量的方式


示例:

再运行程序,即可。

app = Flask(__name__)

app.config.from_envvar('PROJECT_SETTING', silent=True)

@app.route("/")
def index():
print(app.config['SECRET_KEY'])
return "hello world"

关于silent参数的说明:

表示系统环境变量中没有设置相应值时是否抛出异常

  • False 表示不安静的处理,没有值时报错通知,默认为False
  • True 表示安静的处理,即时没有值也让Flask正常的运行下去

访问测试

项目中的常用方式

使用工厂模式创建Flask app,并结合使用配置对象与环境变量加载配置

  • 使用配置对象加载默认配置
  • 使用环境变量加载不想出现在代码中的敏感配置信息
def create_flask_app(config):
"""
创建Flask应用
:param config: 配置对象
:return: Flask应用
"""
app = Flask(__name__)
app.config.from_object(config) # 从环境变量指向的配置文件中读取的配置信息会覆盖掉从配置对象中加载的同名参数
app.config.from_envvar("PROJECT_SETTING", silent=True)
return app

方便了程序的使用,是一种常用的方式

2.3.3 app.run 参数

可以指定运行的主机IP地址,端口,是否开启调试模式

app.run(host="0.0.0.0", port=5000, debug = True)

关于DEBUG调试模式

  1. 程序代码修改后可以自动重启服务器
  2. 在服务器出现相关错误的时候可以直接将错误信息返回到前端进行展示

2.4 开发服务器启动方式

在1.0版本之后,Flask调整了开发服务器的启动方式,由代码编写app.run()语句调整为命令flask run启动。

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
return 'Hello World' # 程序中不用再写app.run()

启动

# 指定FLASK_APP环境变量(Windows CMD环境),demo01是文件名
set FLASK_APP=demo01
# 执行
flask run

说明

  • 环境变量 FLASK_APP 指明flask的启动实例

  • flask run -h 0.0.0.0 -p 8000 绑定地址 端口

  • flask run --help获取帮助

  • 生产模式与开发模式的控制

    通过FLASK_ENV环境变量指明

    • set FLASK_ENV=production 运行在生产模式,未指明则默认为此方式
    • set FLASK_ENV=development运行在开发模式

扩展:指定参数运行

flask run --host 127.0.0.1 --port 6666

3 路由与蓝图

3.1 路由

3.1.1 查询路由信息

需要提前设置好FLASK_APP环境变量的值,并且cmd处于管理员模式

命令行方式

flask routes

在程序中获取

在应用中的url_map属性中保存着整个Flask应用的路由映射信息,可以通过读取这个属性获取路由信息

print(app.url_map)

在程序中遍历路由信息

for rule in app.url_map.iter_rules():
print('name={} path={}'.format(rule.endpoint, rule.rule))

测试

实现通过访问/routes地址,以json的方式返回应用内的所有路由信息

import json
from flask import Flask app = Flask(__name__) @app.route("/flaskRoutes")
def getRoutes():
context = {}
for rule in app.url_map.iter_rules():
context[rule.endpoint] = rule.rule
return json.dumps(context)

3.1.2 指定请求方式

在 Flask 中,定义路由其默认的请求方式为:

  • GET
  • OPTIONS(自带)
  • HEAD(自带)

利用methods参数可以自己指定一个接口的多个请求方式,@app.get()指定的是get请求方式,@app.post()指定的是post请求方式,以此类推

这里只测试了两个,其余的基本相似(这里采用的是Apifox进行测试)

3.2 蓝图

如果一个大的项目有多个模块,在Flask中可以采用蓝图进行开发,对蓝图的理解可以对比Django中的子应用。在Flask中,使用蓝图Blueprint来分模块组织管理。

3.2.1 蓝图的特点

蓝图实际可以理解为是一个存储一组视图方法的容器对象,其具有如下特点:

  • 一个应用可以具有多个Blueprint
  • 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/user”、“/goods”
  • Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
  • 在一个应用初始化时,就应该要注册需要使用的Blueprint

但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。

3.2.2 蓝图的使用方式

使用蓝图可以分为三个步骤

  1. 创建一个蓝图对象

    # 1.创建蓝图对象,两个参数分别为蓝图名称(name)和import_name
    user_bp =Blueprint('user',__name__)
  2. 在这个蓝图对象上进行操作,注册路由,指定静态文件夹,注册模版过滤器

    # 2.蓝图对象注册路由
    @user_bp.get("/user")
    def user_profile():
    return "user_profile"
  3. 在应用对象上注册这个蓝图对象

    # 3.在app中注册蓝图对象
    app.register_blueprint(user_bp)

3.2.3 单文件蓝图

可以将创建蓝图对象与定义视图放到一个文件中 ,也就是按照上面看到的代码

3.2.4 目录(包)蓝图

对于一个打算包含多个文件的蓝图,通常将创建蓝图对象放到Python包的__init__.py文件中

--------- project # 工程目录
|------ main.py # 启动文件
|------ user #用户蓝图
| |--- __init__.py # 此处创建蓝图对象
| |--- view.py

注意:如果不在__init__.py中导入view.py文件,则会导致404错误

3.2.5 扩展用法

1 指定蓝图的url前缀

在应用中注册蓝图时使用url_prefix参数指定前缀

app.register_blueprint(user_bp,url_prefix="/user")

2 蓝图内部静态文件

和应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要我们在 创建时指定 static_folder 参数。

下面的示例将蓝图所在目录下的user_admin目录设置为静态目录

user_bp = Blueprint("user",__name__,static_folder="user_admin")
app.register_blueprint(user_bp,url_prefix="/user")

现在就可以使用/user/user_admin/<path:filename>访问user_admin目录下的静态文件了。

也可通过static_url_path改变访问路径

user_bp = Blueprint("user",__name__,static_folder="user_admin",static_url_path="static")

app.register_blueprint(user_bp,url_prefix="/user")

3 蓝图内部模板目录

蓝图对象默认的模板目录为系统的模版目录,可以在创建蓝图对象时使用template_folder关键字参数设置模板目录

user_bp = Blueprint("user",__name__,
static_folder="user_admin",
static_url_path="static",
template_folder="templates")

4 请求与响应

4.1 处理请求

请求携带的数据可能出现在HTTP报文中的不同位置,需要使用不同的方法来获取参数。

4.1.1. URL路径参数(动态路由)

Flask不同于Django直接在定义路由时编写正则表达式的方式,而是采用转换器语法:

# 这里本质上进行的是正则匹配
@app.route('/users/<user_id>')
def user_info(user_id):
print(type(user_id))
return 'hello user {}'.format(user_id)

此处的<>即是一个转换器,默认为字符串类型,即将该位置的数据以字符串格式进行匹配、并以字符串为数据类型类型、 user_id为参数名传入视图。

Flask也提供其他类型的转换器

DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}

将上面的例子以整型匹配数据,可以如下使用:

@app.route('/users/<int:user_id>')
def user_info(user_id):
print(type(user_id))
return 'hello user {}'.format(user_id)

自定义转换器

如果遇到需要匹配提取/users/18512345678中的手机号数据,Flask内置的转换器就无法满足需求,此时需要自定义转换器。

定义方法

自定义转换器主要做3步

  1. 创建转换器类,保存匹配时的正则表达式

    from werkzeug.routing import BaseConverter
    
    # 1.自定义匹配手机号的转换器
    class PhoneConverter(BaseConverter):
    regex = "^1[3456789]\d{9}$"
    • 注意regex名字固定
  2. 注册转换器,将自定义的转换器告知Flask应用

    # 2. 注册转换器
    app.url_map.converters["phone"] = PhoneConverter
  3. 使用转换器:在使用转换器的地方定义使用

    @app.get('/users/phone/<phone:phoneNum>')
    def getPhone(phoneNum):
    return f"你的手机号是:{phoneNum}"

4.1.2. 其他参数

如果想要获取其他地方传递的参数,可以通过Flask提供的request对象来读取。

不同位置的参数都存放在request的不同属性中

属性 说明 类型
data 记录请求的数据,并转换为字符串 *
form 记录请求中的表单数据 MultiDict
args 记录请求中的查询参数 MultiDict
cookies 记录请求中的cookie信息 Dict
headers 记录请求中的报文头 EnvironHeaders
method 记录请求使用的HTTP方法 GET/POST
url 记录请求的URL地址 string
files 记录请求上传的文件 *

例如 想要获取请求/articles?channel_id=1channel_id的参数,可以按如下方式使用:

@app.get("/request/change")
def getChangeId():
changeId = request.args.get("change_id")
return f"change_id = {changeId}"

上传图片

客户端上传图片到服务器,并保存到服务器中

@app.post("/request/images")
def putImages():
image = request.files.get("image")
image.save("./Saber.png")
return "图片上传成功"

4.2 处理响应

4.2.1 返回模板

使用render_template方法渲染模板并返回

# 其中template表示的是html模板的名称,*kwargs是传递的参数
render_template(template,*kwargs)

实例:

测试:

4.2.2 重定向

from flask import redirect

@app.get("/redirectTest/")
def redirectTest():
return redirect("https://www.baidu.com/")

4.2.3 返回JSON

from flask import jsonify

@app.get("/jsonTest/")
def jsonTest():
json_dict = {
"user_id": 10086,
"user_name": "张三",
"user_age": 30
}
return jsonify(json_dict)

4.2.4 自定义状态码和响应头

1 元组方式

可以返回一个元组,这样的元组必须是

 # 其中response为相应内容,status响应状态码,headers为响应头的添加内容
(response, status, headers)

的形式,且至少包含一个元素。 status 值会覆盖状态代码, headers 可以是一个列表或字典,作为额外的消息标头值。

# 第一种自定义响应头的方式
@app.get("/demoTestOne/")
def demoTestOne():
return ("状态码为 202",202)

2 make_response方式

@app.get("/demoTestTwo/")
def demoTestTwo():
resp = make_response("状态码为 201")
resp.headers['name'] = "zhangsan"
resp.status = 201
return resp

4.3 Cookie与Session

4.3.1 Cookie

1 设置Cookie

@app.get("/setCookie/")
def setCookie():
resp = make_response("set Cookie")
resp.set_cookie("username","zhangsan")
return resp

2 设置Cookie有效期

# 设置Cookie有效期
@app.get("/setCTime")
def setCTime():
resp = make_response("set cookie time")
resp.set_cookie("username","zhangsan",max_age=180)
return resp

3 读取Cookie

# 读取Cookie
@app.get("/getCookie")
def getCookie():
requ = request.cookies.get("username")
return f"username:{requ}"

4 删除Cookie(实际是就是将有效期设置为0)

# 删除Cookie
@app.get("/deleteCookie")
def deleteCookie():
resp = make_response("delete Cookie")
resp.delete_cookie("username")
return resp

4.3.2 Session

1 需要先设置SECRET_KEY

# 1 设置SECRET_KEY
class DefaultConfig(object):
SECRET_KEY = "zhansganlisiwangu"
app.config.from_object(DefaultConfig)

2 设置Session

# 2 设置Session
from flask import session
@app.get("/setSession/")
def setSession():
session['username'] = "username"
return "set Session"

3 读取Session

@app.get("/getSession/")
def getSession():
username = session.get("username")
return f"get session username {username}"

这里的Session并不是真正意义上的session,而是伪装成Session的Cookie,因为它实际上并没有将值存储在服务器上

5 请求钩子与上下文

5.1 异常处理

5.1.1 HTTP 异常主动抛出

  • abort 方法

    • 抛出一个给定状态代码的 HTTPException 或者 指定响应,例如想要用一个页面未找到异常来终止请求,你可以调用 abort(404)。
  • 参数:
    • code – HTTP的错误状态码
# abort(404)
abort(500)

抛出状态码的话,只能抛出 HTTP 协议的错误状态码

from flask import abort

@app.get("/getExcetion")
def getExcetion():
print("异常请求测试")
abort(500)
return "500异常测试"

5.1.2 捕获错误

errorhandler 装饰器

  • 注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法

参数:

  • code_or_exception – HTTP的错误状态码或指定异常

  • 例如统一处理状态码为500的错误给用户友好的提示:

# 捕获异常
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
return "除数不能为0" @app.get("/zeroTest")
def zeroDivision():
a = 10086/0
return f"结果是:{a}"

  • 捕获指定异常
@app.errorhandler(500)
def errorCode500():
return "呜呜呜,服务器出错了,请稍后访问" @app.get("/getExcetion")
def getExcetion():
print("异常请求测试")
abort(500)
return "500异常测试"

5.2 请求钩子

5.2.1 请求钩子概念

在客户端和服务器交互的过程中,有些准备工作或扫尾工作需要处理,比如:

  • 在请求开始时,建立数据库连接;
  • 在请求开始时,根据需求进行权限校验;
  • 在请求结束时,指定数据的交互格式;

为了让每个视图函数避免编写重复功能的代码,Flask提供了通用设施的功能,即请求钩子。


请求钩子是通过装饰器的形式实现,Flask支持如下三种请求钩子(before_first_request已经被废止):

1 before_request

  • 在每次请求前执行
  • 如果在某修饰的函数中返回了一个响应,视图函数将不再被调用

2 after_request

  • 如果没有抛出错误,在每次请求后执行
  • 接受一个参数:视图函数作出的响应
  • 在此函数中可以对响应值在返回之前做最后一步修改处理
  • 需要将参数中的响应在此参数中进行返回

3 teardown_request

  • 在每次请求后执行
  • 接受一个参数:错误信息,如果有相关错误抛出

5.2.2 代码测试

# 在每一次请求之前调用,这时候已经有请求了,可能在这个方法里面做请求的校验
# 如果请求的校验不成功,可以直接在此方法中进行响应,直接return之后那么就不会执行视图函数
@app.before_request
def before_request():
print("before_request")
# 在执行完视图函数之后会调用,并且会把视图函数所生成的响应传入,可以在此方法中对响应做最后一步统一的处理
@app.after_request
def after_request(response):
print("after_request")
response.headers["Content-Type"] = "application/json"
return response
# 请每一次请求之后都会调用,会接受一个参数,参数是服务器出现的错误信息
@app.teardown_request
def teardown_request(response):
print("teardown_request")
# 测试请求
@app.route('/testRequest')
def index():
return 'index'

请求时打印的内容,可以看出,三个钩子函数都被调用

5.3 上下文

上下文:即语境,语意,在程序中可以理解为在代码执行到某一时刻时,根据之前代码所做的操作以及下文即将要执行的逻辑,可以决定在当前时刻下可以使用到的变量,或者可以完成的事情。

Flask中有两种上下文,请求上下文和应用上下文

Flask中上下文对象:相当于一个容器,保存了 Flask 程序运行过程中的一些信息。

5.3.1 请求上下文(request context)

在 flask 中,可以直接在视图函数中使用 request 这个对象进行获取相关数据,而 request 就是请求上下文的对象,保存了当前本次请求的相关数据,请求上下文对象有:request、session

1 request

  • 封装了HTTP请求的内容,针对的是http请求。举例:user = request.args.get('user'),获取的是get请求的参数。
# request获取请求参数
@app.get("/requestContext")
def requestContext():
user = request.args.get('user')
return f"你的名字是:{user}"

2 session

  • 用来记录请求会话中的信息,针对的是用户信息。举例:session['name'] = user.id,可以记录用户信息。还可以通过session.get('name')获取用户信息。
# session存储用户id
@app.get("/setRequestSession")
def setRequestSession():
user_id = "zhangsan"
session['name'] = user_id
return "Session 设置成功" # 获取Session
@app.get("/getRequestSession")
def setRequestSession():
username = session.get('name')
return f"你的名字是:{username}"

5.3.2 应用上下文(application context)

它的字面意思是 应用上下文,但它不是一直存在的,它只是request context 中的一个对app的代理(人),所谓local proxy。它的作用主要是帮助 request 获取当前的应用,它是伴 request 而生,随 request 而灭的。

应用上下文对象有:current_appg

1 current_app

应用程序上下文,用于存储应用程序中的变量,可以通过current_app.name打印当前app的名称,也可以在current_app中存储一些变量,例如:

  • 应用的启动脚本是哪个文件,启动时指定了哪些参数
  • 加载了哪些配置文件,导入了哪些配置
  • 连了哪个数据库
  • 有哪些public的工具类、常量
  • 应用跑再哪个机器上,IP多少,内存多大

示例

创建current_app_demo.py

from flask import current_app

app1 = Flask(__name__)
app2 = Flask(__name__) app1.redis_cli = 'app1 redis client'
app2.redis_cli = 'app2 redis client' @app1.route('/route11')
def route11():
# current_app获取的是当前正在使用的app
return current_app.redis_cli @app2.route('/route21')
def route21():
return current_app.redis_cli

运行

set FLASK_APP=main:app1
flask run

set FLASK_APP=main:app2
flask run

作用

current_app 就是当前运行的flask app,在代码不方便直接操作flask的app对象时,可以操作current_app就等价于操作flask app对象,而不会出现app调用app对象自身导致的循环依赖问题

2 g对象

g 作为flask程序全局的一个临时变量,充当中间媒介的作用,我们可以通过它在一次请求调用的多个函数间传递一些数据。每次请求都会重设这个变量。

示例

from flask import g
def query():
user_id = g.user_id
user_name = g.user_name
return f"userid={user_id},name={user_name},已执行内部函数" @app.post("/getUser/")
def get_user():
userid = request.args.get("userid")
username = request.args.get("username")
g.user_id = userid
g.user_name = username
text = query()
return text

5.3.3 app_context 与 request_context

思考

在Flask程序未运行的情况下,调试代码时需要使用current_appgrequest这些对象,该如何进行测试。

1 app_context

app_context为我们提供了应用上下文环境,允许我们在外部使用应用上下文current_appg`

可以通过with语句进行使用

>>> from flask import Flask
>>> app = Flask('')
>>> app.redis_cli = 'redis client'
>>>
>>> from flask import current_app
>>> current_app.redis_cli # 错误,没有上下文环境
报错
>>> with app.app_context(): # 借助with语句使用app_context创建应用上下文
... print(current_app.redis_cli)
...
redis client

2 request_context

request_context为我们提供了请求上下文环境,允许我们在外部使用请求上下文requestsession

可以通过with语句进行使用

>>> from flask import Flask
>>> app = Flask('')
>>> request.args # 错误,没有上下文环境
报错
>>> environ = {'wsgi.version':(1,0), 'wsgi.input': '', 'REQUEST_METHOD': 'GET', 'PATH_INFO': '/', 'SERVER_NAME': 'itcast server', 'wsgi.url_scheme': 'http', 'SERVER_PORT': '80'} # 模拟解析客户端请求之后的wsgi字典数据
>>> with app.request_context(environ): # 借助with语句使用request_context创建请求上下文
... print(request.path)

6 Flask-RESTful

6.1 起步

Flask-RESTful是用于快速构建REST API的Flask扩展。

6.1.1 安装

pip install flask-restful

6.1.2 Hello World

from flask import Flask
from flask_restful import Resource,Api app = Flask(__name__)
api = Api(app) class HelloWordResource(Resource):
def get(self):
return {"hello":"world"}
def post(self):
return {"msg":"post Hello,World"} api.add_resource(HelloWordResource,"/hello") # 也可以选择flask run的启动方式
if __name__=="__main__":
app.run(debug=True)

6.2 关于视图

6.2.1 为路由起名

通过endpoint参数为路由起名

# 这个名称在后续会用到
api.add_resource(HelloWorldResource, '/', endpoint='HelloWorld')

6.2.2 蓝图中使用

# 1 定义蓝图
user_bp = Blueprint('user',__name__,url_prefix="/user")
# 2 api配置蓝图
user_api = Api(user_bp)
# 3 app配置蓝图
app.register_blueprint(user_bp)
# 4 定义蓝图请求
class UserBlueResource(Resource):
def get(self):
context = {
"massage":2001,
"data":{
"name":"zhangsan",
"age":16
}
}
return context
def post(self):
context = {
"massage":2001,
"data":{
"name":"This is post Man",
"age":"*****(查看请充值vip)"
}
}
return context
# 5 配置api路由
user_api.add_resource(UserBlueResource,"/blueUser")

6.2.3 装饰器

使用method_decorators添加装饰器

为类视图中的所有方法添加装饰器

def decorator1(func):
def wrapper(*args, **kwargs):
print('装饰器1正在执行')
return func(*args, **kwargs)
return wrapper def decorator2(func):
def wrapper(*args, **kwargs):
print('装饰器2正在执行')
return func(*args, **kwargs)
return wrapper class DemoRequest(Resource):
method_decorators = [decorator1,decorator2]
def get(self):
context = {
"msg":200,
"data":{
"text":"一切正常"
}
}
return context def post(self):
context = {
"msg":200,
"data":{
"text":"I am PostMax"
}
}
return context
api.add_resource(DemoRequest,"/demo")


为类视图中不同的方法添加不同的装饰器

  class DemoResource(Resource):
method_decorators = {
'get': [decorator1, decorator2],
'post': [decorator1]
} # 使用了decorator1 decorator2两个装饰器
def get(self):
return {'msg': 'get view'} # 使用了decorator1 装饰器
def post(self):
return {'msg': 'post view'} # 未使用装饰器
def put(self):
return {'msg': 'put view'}

get请求测试

post请求测试

put请求测试

6.3 关于请求

6.3.1 概述

Flask-RESTful 提供了RequestParser类,用来帮助我们检验和转换请求数据。

6.3.2 使用步骤:

  1. 创建RequestParser对象

  2. RequestParser对象中添加需要检验或转换的参数声明

  3. 使用parse_args()方法启动检验处理

  4. 检验之后从检验结果中获取参数时可按照字典操作或对象属性操作

    args.rate

    args['rate']

6.3.3 参数说明

1 required

描述请求是否一定要携带对应参数,默认值为False

  • True 强制要求携带

    若未携带,则校验失败,向客户端返回错误信息,状态码400

  • False 不强制要求携带

    若不强制携带,在客户端请求未携带参数时,取出值为None

from flask_restful.reqparse import RequestParser
class rqparseRequest(Resource):
# 1 required 参数测试
def get(self):
rqp = RequestParser()
# 强制要求携带该参数
rqp.add_argument("user",required=True)
args = rqp.parse_args()
return {"message":200,"data":{"user":args.user}}
api.add_resource(rqparseRequest,"/rpone")

2 help

参数检验错误时返回的错误描述信息

from flask_restful.reqparse import RequestParser
class rqparseRequest(Resource):
# 2 help参数测试
def post(self):
rqp = RequestParser()
# 强制要求携带该参数
rqp.add_argument("user",required=True,help="没有user参数")
args = rqp.parse_args()
return {"message":200,"data":{"user":args.user}}
api.add_resource(rqparseRequest,"/rpone")

3 action

描述对于请求参数中出现多个同名参数时的处理方式

  • action='store' 保留出现的第一个, 默认
  • action='append' 以列表追加保存所有同名参数的值
from flask_restful.reqparse import RequestParser
class rqparseRequest(Resource):
# 3 action参数测试
def put(self):
rp = RequestParser()
rp.add_argument("user",required=True,action="append")
args = rp.parse_args()
return {"message": 200, "data": {"user": args.user}}
api.add_resource(rqparseRequest,"/rpone")

4 type

描述参数应该匹配的类型,可以使用python的标准数据类型string、int,也可使用Flask-RESTful提供的检验方法,还可以自己定义

  • 标准类型

    from flask_restful.reqparse import RequestParser
    class rqparseRequest(Resource):
    # 4 type参数测试
    def delete(self):
    rp = RequestParser()
    rp.add_argument("user",required=True,type=int)
    args = rp.parse_args()
    return {"message": 200, "data": {"user": args.user}}
    api.add_resource(rqparseRequest,"/rpone")

  • Flask-RESTful提供

    检验类型方法在flask_restful.inputs模块中

    • url

    • regex(指定正则表达式)

      from flask_restful import inputs
      
      class rqparseRequestTwo(Resource):
      # Flask指定的检验格式
      def get(self):
      rp = RequestParser()
      rp.add_argument('user', type=inputs.regex(r'^1[3-9]\d{9}$'))
      args = rp.parse_args()
      return {"message": 200, "data": {"user": args.user}}
      api.add_resource(rqparseRequestTwo,"/rptwo")

    • natural 自然数0、1、2、3...

    • positive 正整数 1、2、3...

    • int_range(low ,high) 整数范围

      rp.add_argument('a', type=inputs.int_range(1, 10))
    • boolean

  • 自定义

    def mobile(mobile_str):
    """
    检验手机号格式
    :param mobile_str: str 被检验字符串
    :return: mobile_str
    """
    if re.match(r'^1[3-9]\d{9}$', mobile_str):
    return mobile_str
    else:
    raise ValueError('{} is not a valid mobile'.format(mobile_str)) rp.add_argument('a', type=mobile)

5 location

描述参数应该在请求数据中出现的位置

# Look only in the POST body
parser.add_argument('name', type=int, location='form') # Look only in the querystring
parser.add_argument('PageSize', type=int, location='args') # From the request headers
parser.add_argument('User-Agent', location='headers') # From http cookies
parser.add_argument('session_id', location='cookies') # From json
parser.add_argument('user_id', location='json') # From file uploads
parser.add_argument('picture', location='files')

也可指明多个位置

parser.add_argument('text', location=['headers', 'json'])

测试

from flask_restful import inputs

class rqparseRequestTwo(Resource):
# location测试
def post(self):
parser = RequestParser()
# form表单
parser.add_argument('name', type=int, location='form')
# 位置参数
parser.add_argument('PageSize', type=int, location='args')
args = parser.parse_args()
return {"message": 200, "data": {"name": args.name,"PageSize": args.PageSize}}
api.add_resource(rqparseRequestTwo,"/rptwo")

6.4 关于响应

6.4.1 序列化数据

Flask-RESTful 提供了marshal工具,用来帮助我们将数据序列化为特定格式的字典数据,以便作为视图的返回值。

# 1 导包
from flask_restful import Resource,fields,marshal_with,marshal
# 2 定义类
class Book(object):
def __init__(self,bookName,author):
self.bookName = bookName
self.author = author
# 3 定义模板
resource_fields = {
'bookName':fields.String,
"author":fields.String
} # 4 定义返回函数
class BookTodo(Resource):
# 使用装饰器实现
@marshal_with(resource_fields,envelope="context")
def get(self):
# 对象无法直接返回,这个装饰器相当于将对象序列化了
book = Book("《射雕英雄传》","金庸")
return book
api.add_resource(BookTodo,"/booktodo")

也可以不使用装饰器的方式

# 不使用装饰器,而是调用函数实现
def post(self):
book = Book("《射雕英雄传》","金庸")
return marshal(book,resource_fields,envelope="contextPost")

还可以直接自己实现

# 直接自己写,单次返回更简单
def put(self):
book = Book("《射雕英雄传》", "金庸")
context = {
"context": {
"bookName": book.bookName,
"author": book.author
}
}
return context

6.4.2 定制返回的JSON格式

1 需求

想要接口返回的JSON数据具有如下统一的格式

{"message": "描述信息", "data": {要返回的具体数据}}

在接口处理正常的情况下, message返回ok即可,但是若想每个接口正确返回时省略message字段

class DemoResource(Resource):
def get(self):
return {'user_id':1, 'name': 'itcast'}

对于诸如此类的接口,能否在某处统一格式化成上述需求格式?

{"message": "OK", "data": {'user_id':1, 'name': 'itcast'}}

2 解决

Flask-RESTful的Api对象提供了一个representation的装饰器,允许定制返回数据的呈现格式

api = Api(app)

@api.representation('application/json')
def handle_json(data, code, headers):
# TODO 此处添加自定义处理
return resp

Flask-RESTful原始对于json的格式处理方式如下:

代码出处:flask_restful.representations.json


from flask import make_response, current_app
from flask_restful.utils import PY3
from json import dumps def output_json(data, code, headers=None):
"""Makes a Flask response with a JSON encoded body""" settings = current_app.config.get('RESTFUL_JSON', {}) # If we're in debug mode, and the indent is not set, we set it to a
# reasonable value here. Note that this won't override any existing value
# that was set. We also set the "sort_keys" value.
if current_app.debug:
settings.setdefault('indent', 4)
settings.setdefault('sort_keys', not PY3) # always end the json dumps with a new line
# see https://github.com/mitsuhiko/flask/pull/1262
dumped = dumps(data, **settings) + "\n" resp = make_response(dumped, code)
resp.headers.extend(headers or {})
return resp

为满足需求,做如下改动即可

@api.representation('application/json')
def output_json(data, code, headers=None):
"""Makes a Flask response with a JSON encoded body"""
# --------------------此处为自己添加--------------------------
if 'message' not in data:
data = {
'message': 'OK',
'data': data
}
# ---------------------------------------------------------
settings = current_app.config.get('RESTFUL_JSON', {})
if current_app.debug:
settings.setdefault('indent', 4)
settings.setdefault('sort_keys', not PY3)
dumped = dumps(data, **settings) + "\n" resp = make_response(dumped, code)
resp.headers.extend(headers or {})
return resp

测试代码

# 测试代码
class userTest(Resource):
def get(self):
return {"name":"zhangsan"} api.add_resource(userTest,"/userTest")

Flask2.x新变化

嵌套蓝图(#3923

对于一个比较大的项目,一般会使用蓝本来组织不同的模块。而如果你的项目非常大,那么嵌套蓝本就可以派上用场了。借助嵌套蓝本支持,你可以在某个蓝本之内再创建多个子蓝本,对项目进行多层模块化组织(而且支持无限嵌套,你可以嵌套很多层):

 parent = Blueprint("parent", __name__)  # 创建父蓝本
child = Blueprint("child", __name__) # 创建子蓝本
parent.register_blueprint(child, url_prefix="/child") # 把子蓝本注册到父蓝本上
app.register_blueprint(parent, url_prefix="/parent") # 把父蓝本注册到程序实例上

这样在生成子蓝本的 URL 时需要传入完整的端点链:

 url_for('parent.child.create')
/parent/child/create

这个特性来源于一个 2012 年创建的 feature request issue

基本的 async/await 支持

Flask 2.0 带来了基本的异步支持,现在你可以定义异步视图(以及异步错误处理函数、异步请求钩子函数):

 import asyncio
from flask import Flask app = Flask(__name__) @app.route('/')
async def say_hello():
await asyncio.sleep(1)
return {'message': 'Hello!'}

注意要先安装额外依赖:

 pip install -U flask[async]

顺便说一句,如果你在 Windows 上使用 Python 3.8,那么会有一个来自 Python 或 asgiref 的 bug 导致出错:ValueError: set_wakeup_fd only works in main thread。可以通过下面两种方式(任选一种)处理(具体参考这个 SO 回答):

  • 升级到 Python 3.9
  • 在你的入口脚本顶部添加临时修复代码:
# top of the file
import sys, asyncio if sys.platform == "win32" and (3, 8, 0) <= sys.version_info < (3, 9, 0)::
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())

不过目前只是一个基于 asgiref 的异步实现,作为异步支持的第一步,后续还会进行更多的优化和改进,更多相关信息可以参考文档

快捷路由装饰器(#3907

新增了下面的快捷路由装饰器:

  • app.get()
  • app.post()
  • app.delete()
  • app.put()
  • app.patch()

举例来说,使用 app.post() 等同于 app.route(methods=['POST'])

 from flask import Flask

 app = Flask(__name__)

 @app.post('/')
def index():
return {'message': 'Hello!'}

注意不要在这些快捷装饰器里传入 methods 参数。如果需要在单个视图处理多个方法的请求,使用 app.route()

我在某次 pallets 会议上提议添加这些装饰器时一开始是被拒绝的,后来 Phil Jones 创建了 #3907 经过二次讨论后才最终合并(被拒绝后我就把当时正在开发的 APIFlask 从扩展改成了继承 Flask 基类的框架,然后加了这些装饰器)。

其他变动

  • 优化了浏览器缓存控制,对 CSS、图片等静态文件做出的变动会在程序重载后立刻更新,不再需要手动清除页面缓存。
  • Werkzeug 的 multipart 解析(尤其是大文件上传处理)性能提高了 15 倍。
  • 配置对象增加 Config.from_file() 方法支持从任意文件加载器导入配置(比如 toml.loadjson.load),未来会取代 Config.from_json() 方法。
  • 在使用环境变量 FLASK_APP 指定工厂函数时支持传入关键字参数。
  • flask shell 支持 tab 和历史补全(需要安装 readline)。
  • CLI 系统优化了找不到程序时的错误处理和错误输出显示,同时修正了 Windows 上的命令行颜色输出。

8 最全的零基础Flask教程的更多相关文章

  1. WebRTC 零基础开发者教程(中文版)下载

    WebRTC 简介 WebRTC,是一个支持网页浏览器进行实时语音通话或视频聊天的技术,是谷歌2010年以6820万美元收购Global IP Solutions公司而获得的一项技术. WebRTC提 ...

  2. 大牛整理最全Python零基础入门学习资料

    大牛整理最全Python零基础入门学习资料 发布时间:『 2017-11-12 11:56 』     帖子类别:『人工智能』  阅读次数:3504 (本文『大牛整理最全Python零基础入门学习资料 ...

  3. WordPress安装:零基础入门教程

    WordPress安装:零基础入门教程 1主机空间要求要运行 WordPress,主机空间需满足以下条件.不过现在网络上的空间基本都可以,而且还让你随意定制Php和Mysql版本,至于空间和数据库大小 ...

  4. Python运算符 - Python零基础入门教程

    目录 一.算术运算符 二.赋值运算符 三.比较运算符 四.运算符的优先等级 五.重点总结 六.猜你喜欢 零基础 Python 学习路线推荐 : Python 学习目录 >> Python ...

  5. Python break/continue - Python零基础入门教程

    目录 一.break 二.continue 三.重点总结 四.猜你喜欢 零基础 Python 学习路线推荐 : Python 学习目录 >> Python 基础入门 在 Python wh ...

  6. Python for循环 - Python零基础入门教程

    目录 一.for 循环语法 二.for 循环实战 三.重点总结 四.猜你喜欢 零基础 Python 学习路线推荐 : Python 学习目录 >> Python 基础入门 在 Python ...

  7. node.js零基础详细教程(7.5):mongo可视化工具webstorm插件、nodejs自动重启模块Node Supervisor(修改nodejs后不用再手动命令行启动服务了)

    第七章 建议学习时间4小时  课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑. ...

  8. gulp菜鸟级零基础详细教程

    : 相信大家一定听说过gulp或者webpack,grunt等前端构建工具.gulp是前端开发过程中对代码进行构建的工具,是自动化项目的构建利器:她不仅能对网站资源进行优化,而且在开发过程中很多重复的 ...

  9. 灵魂画手的零基础python教程1:关于Python学习的误区、python的优缺点、前景

    滴~ 近段时间,因为工作项目的原因,阿菌要重拾起python这门语言了,所以顺势写一门python教程,精心的编排,配上漫画和视频,希望能帮助更多想接触编程的同学入门,课程将从基础语法开始讲起,和大家 ...

  10. nodejs零基础详细教程2:模块化、fs文件操作模块、http创建服务模块

    第二章  建议学习时间4小时  课程共10章 学习方式:详细阅读,并手动实现相关代码 学习目标:此教程将教会大家 安装Node.搭建服务器.express.mysql.mongodb.编写后台业务逻辑 ...

随机推荐

  1. 读《图解HTTP》

    最近读了一本书<图解HTTP>,读完后在大体上对HTTP协议有了更深层次的了解.以下是我以前不懂的问题,通过阅读此书后,这些问题都有了答案: 问题: URI和URL的区别? cookie到 ...

  2. GE反射内存实时通讯网络解决方案

    时通讯网络是用于需要较高实时性要求的应用领域的专用网络通讯技术,一般采用基于高速网络的共享存储器技术实现.它除了具有严格的传输确定性和可预测性外,还具有速度高.通信协议简单.宿主机负载轻.软硬件平台适 ...

  3. 【Vue2】NavigationDuplicated: Avoided redundant navigation to current location:xxxxx

    翻译过来就是,导航重复:避免了到当前位置的冗余导航. 简单来说就是重复跳转了相同路径 原因 触发这种情况是因为vue-router中引入了primise,当传递了多次重复的参数就会抛出异常,而这种问题 ...

  4. flutter apk启动闪退问题

    今发布一个flutter apk 安装后启动时老是闪退,经过一遍又一遍查找,发现是指定了so的问题 看多次点击启动 一.比对打包后的apk 在出现该问题后也搜索了不少资料,参考过 https://bl ...

  5. 为teamcity的代码语法检查工具pyflakes增加支持python2和python3

    TeamCity和pyflakes TeamCity是一款由JetBrains公司开发的持续集成和部署工具,它提供了丰富的功能来帮助团队协作进行软件开发.其中包括代码检查.自动化构建.测试运行.版本控 ...

  6. [Java SE] 彻底搞懂Java程序的三大参数配置途径:系统变量与JVM参数(VM Option)/环境变量/启动程序参数args

    0 序言 一次没搞懂,处处受影响.这个问题属于基础问题,但又经常踩坑,不得不重视一下了. 1 Java程序动态参数的配置途径:系统变量与JVM参数(VM Option) vs 环境变量 vs 启动程序 ...

  7. debug.exe的使用

    debug.exe的使用 debug.exe 是 Windows 操作系统自带的一个命令行调试工具,用于在 DOS 环境下进行汇编语言级别的调试操作.它可以让程序员以来自底层的方式逐步执行代码并检查每 ...

  8. gitlab docker升级报错

    背景 使用docker部署gitlab(9.5.4)后,发现合并代码有问题 日志: 看gitlab官网此问题已修复,由于上传了一批代码,又懒得重建,决定对gitlab升级 docker启动命令: do ...

  9. requests Python中最好用的网络请求工具 基础速记+最佳实践

    简介 requests 模块是写python脚本使用频率最高的模块之一.很多人写python第一个使用的模块就是requests,因为它可以做网络爬虫.不仅写爬虫方便,在日常的开发中更是少不了requ ...

  10. JAVA获取字符串内的括号对;获取括号对的内容;按指定规则返回括号对位置;

    先看结果:处理字符串 "这个是一条测试用的字符串[ ( 5 ( 4( 3 [(1) (2)] ))(7))][(6)]" 结果 解决思路:参考正则表达式里面出入站部分 代码实现如下 ...