Flask框架
1.框架介绍
Flask诞生于2010年,是Armin ronacher(人名)用 Python 语言编写的 **轻量级Web开发框架** ,其本身相当于一个内核,主要实现路**由分发和模板渲染功能**。
Flask中常用的扩展包:
- Flask-SQLalchemy:ORM操作数据库;
- Flask-RESTful:开发REST API的工具;
- Flask-Session:Session存储;
- Flask-Migrate:管理迁移数据库;
- Flask-Caching:缓存;
- Flask-WTF:表单;
- Flask-Mail:邮件;
- Flask-Login:认证用户状态;
- Flask-OpenID:认证;
- Flask-Admin:简单而可扩展的管理接口的框架
- Flask-Bable:提供国际化和本地化支持,翻译;
- Flask-Bootstrap:集成前端Twitter Bootstrap框架;
- Flask-Moment:本地化日期和时间;
2. 基本使用
环境安装
mkvirtualenv flask_env -p python3 #创建名为flask_env的虚拟环境,使用python3解释器
pip install flask==1.0.3 #安装版本为1.0.3的flask包
在pycharm中新建flask项目
新建一个helloflask.py文件
from flask import Flask # 导入flask包
app = Flask(__name__) #创建flask对象,接收一个参数__name__,它会指向程序所在的包
@app.route('/') #定义路由
def index(): #视图
return 'hello flask'
if __name__=='__main__':
app.run() #运行程序
# app.run()中可以添加参数
# host: 绑定的ip(域名) 0.0.0.0
# port: 监听的端口号
# debug: 是否开启调试模式 1> 可以在网页上显示python错误 2> 更新代码后测试服务器自动重启
#app.run(host='0.0.0.0', port=8000, debug=True)
运行结果:
/home/ubuntu/.virtualenvs/flask_env/bin/python /home/ubuntu/PycharmProjects/flask2/helloflask.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Serving Flask app "helloflask" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
第二种运行文件的方式
- flask1.0开始, 类似Django, 通过终端执行封装的脚本命令flask run来运行应用, 不再需要调用app.run()
- 新的形式更加有利于 灵活修改环境配置
export FLASK_APP=xx.py #指定flask应用所在的文件路径
export FLASK_ENV=development #设置项目的环境,默认是生产环境
flask run -h 0.0.0.0 -p 8000 #启动测试服务器并接收请求
- 在ubuntu中可以通过命令查看和修改环境变量
# 获取环境变量
export # 查看所有环境变量
echo $环境变量 # 查看指定的环境变量
# 设置环境变量
export 环境变量名=值 # 给本次会话设置环境变量, 一旦终端关闭, 环境变量会清空
# 将环境变量写入配置文件(.bashrc/.bash_profile), 重新打开终端时再次加载环境变量
第三中运行文件的方式
路由
1)定义路由
- 路由对应的URL必须以 / 开头
- app.route()中可以使用methods方法,作用是指定路由支持的请求方式
- 可以使用app.url_map命令获取所有路由规则
- 路由规则中主要包含url资源段rule,支持的请求方式methods,视图函数标记endpoint三部分
from flask import Flask
app=Flask(__name__)
@app.route('/hello',methods=['get','post'])
def index():
return 'index'
if __name__=='__main__':
print(app.url_map)
# 获取路由信息
print('-----------------------------------')
for rule in app.url_map.iter_rules():
print(rule.rule, rule.methods, rule.endpoint)
# 具体的获取每条路由信息中的项
app.run(debug=True)
运行结果:
Map([<Rule '/hello' (POST, OPTIONS, GET, HEAD) -> index>,
<Rule '/static/<filename>' (OPTIONS, GET, HEAD) -> static>])
-----------------------------------
/hello {
'POST', 'OPTIONS', 'GET', 'HEAD'} index
/static/<path:filename> {
'OPTIONS', 'GET', 'HEAD'} static
网页效果:
2)路由变量
- 用于传递url路径参数,实现动态url
- 格式:/路径/<路由变量>
from flask import Flask
app=Flask(__name__)
@app.route('/user/<userid>')
def index(userid):
print(userid)
return 'index'
if __name__=='__main__':
app.run(debug=True)
使用postment发送get请求:http://127.0.0.1:5000/user/18240118472
运行结果:
18240118472
127.0.0.1 - - [17/Mar/2020 19:49:30] "GET /user/18240118472 HTTP/1.1" 200 -
3)路由转换器
- 作用是对url传递对的参数进行格式校验,类似django设置url时的正则表达式参数
- 格式:/路径/<转换器名:路由变量>
- 所有路由转换器都继承自BaseConverter类
from flask import Flask
app = Flask(__name__)
@app.route('/user/<int:userid>')
def index(userid):
print(userid)
return 'index'
if __name__=='__main__':
app.run()
使用postment分别发送get请求:http://127.0.0.1:5000/user/18240
http://127.0.0.1:5000/user/18240a
运行结果分别为:
18240
127.0.0.1 - - [17/Mar/2020 20:03:42] "GET /user/18240 HTTP/1.1" 200 -
127.0.0.1 - - [17/Mar/2020 20:05:00] "GET /user/18240a HTTP/1.1" 404 -
4)自定义路由转换器
- 路由转换器可以由开发者自定义,自定义路由转换器的步骤
- 定义路由转换器类,继承BaseConverter类
- 设置regex属性(正则匹配规则)
- 为应用添加自定义路由转换器
from flask import Flask
from werkzeug.routing import BaseConverter
app = Flask(__name__)
class CustomConverter(BaseConverter):
regex = '\w{3}' # 3个字符
app.url_map.converters['abc']=CustomConverter
@app.route('/user/<abc:mobile>')
def index(mobile):
print(mobile)
return 'index'
if __name__=='__main__':
print(app.url_map.converters) #打印所有路由转换器
print('------------------')
app.run()
使用postment分别发送get请求:http://127.0.0.1:5000/user/zhu
运行结果:
lt': <class 'werkzeug.routing.UnicodeConverter'>, 'string': <class 'werkzeug.routing.UnicodeConverter'>, 'any': <class 'werkzeug.routing.AnyConverter'>, 'path': <class 'werkzeug.routing.PathConverter'>, 'int': <class 'werkzeug.routing.IntegerConverter'>, 'float': <class 'werkzeug.routing.FloatConverter'>, 'uuid': <class 'werkzeug.routing.UUIDConverter'>, 'abc': <class '__main__.CustomConverter'>}
------------------
zhu
127.0.0.1 - - [17/Mar/2020 20:24:04] "GET /user/zhu HTTP/1.1" 200 -
请求
- flask中的请求数据通过request对象来获取
- 常用的请求属性:
属性 | 说明 | 类型 |
---|---|---|
url | 记录请求的URL地址 | str |
method | 记录请求使用的HTTP方法 | str |
headers | 记录请求中的报文头 | EnvironHeaders 类字典对象 |
args | 记录请求中的查询参数 | MultiDict |
form | 记录请求中的表单数据 | MultiDict |
data | 记录请求的数据,并转换为字符串 | bytes |
json | 记录请求体中的json数据 | Dict |
files | 记录请求上传的文件 | MultiDict[str: FileStorage] |
from flask import Flask, request
# from werkzeug.datastructures import FileStorage
app = Flask(__name__)
@app.route('/', methods=['get', 'post'])
def index():
# 获取请求的基础数据
# print(request.url) # 请求的URL
# print(request.method) # 本次请求的请求方式
# print(request.headers) # 获取请求头信息 类字典对象
# print(request.headers['Host'])
# print(request.headers.get('Host')) # 建议使用get方法, 键不存在不报错
# 请求传递数据 1> URL路径 -> 路由变量 2> 查询字符串 get 3> 请求体 post 4> 请求头 -> request.headers
# 获取查询字符串 -> request.args xx?name=zs&age=20 类字典对象
# print(request.args.get('name'))
# print(request.args.get('age'))
# 请求体: 键值对(表单) 文本(json/xml) 文件(图片/音频)
# 获取post键值对 -> request.form 类字典对象
# print(request.form.get('username'))
# 获取post文本数据 -> request.data / request.json
# print(request.data) # 返回bytes类型
# print(request.json.get('age')) # request.json直接将json字符串转为字典
# 获取post文件 -> request.files 类字典对象
file = request.files.get("avatar") # type: FileStorage
# print(type(file)) # 返回 FileStorage文件对象
# 将文件保存到本地
file.save('4323.jpg')
# 获取文件的二进制数据
# img_bytes = file.read()
# print(img_bytes)
return "index"
if __name__ == '__main__':
app.run(debug=True)
使用postman发送请求:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ovrKPdNn-1586763119996)(http://my-picture-1258042328.cos.ap-shanghai.myqcloud.com/flask%E5%9F%BA%E7%A1%80/20200317090928173.png)]
运行结果:
项目中会新出现一个4323.jpg文件
127.0.0.1 - - [17/Mar/2020 21:05:48] "GET / HTTP/1.1" 200 -
响应
1)访问静态资源
- 可以将静态资源放入项目的static文件夹中,通过内置的静态资源访问路由
- 如 static目录放入文件 123.jpg, 则访问URL为 http://127.0.0.1:5000/static/123.jpg
- 可以通过修改flask对象的初始化参数来修改静态资源的存储和访问路径
- static_folder 修改静态文件的存储目录
- static_url_path 修改静态文件的访问路径
from flask import Flask
app = Flask(__name__, # 导入名称, flask会根据该参数查询静态文件的存储路径
# 官方建议直接使用__name__, 表示从当前目录中查询静态文件存储路径
static_folder="static1", # 设置静态文件的存储目录
static_url_path='/res/img', # 设置静态文件的URL访问路径 如 127.0.0.1:5000/res/img/123.jpg
)
if __name__ == '__main__':
app.run(debug=True)
2)自定义响应对象
- 视图函数返回的str/bytes类型的数据会被自动包装为Response响应对象,也可以通过创建Response响应对象来自定义响应头等信息
from flask import Flask, make_response, Response
app= Flask(__name__)
@app.route('/demo')
def demo():
response = make_response('hello')
response.header['A']=10
return response
if __name__ == '__main__':
app.run()
通过postman查看返回的自定义响应头:
3)返回json
- 如果接口现需要返回json数据,在flask中可以直接使用jsonify()函数生成一个json的响应
- 不推荐时使用json.dumps()直接返回,因为返回的数据要符合 HTTP 协议规范,如果是 JSON 需要指定 content-type:application/json
from flask import Flask, make_response, Response,jsonify
# import json
app = Flask(__name__)
@app.route('demo')
def demo():
dict_={
'name':'zhu','age':24}
return jsonify(dict_)
# return json.dumps(dict_)
if __name__=='__main__':
app.run()
通过postman查看返回的自定义响应:
4)重定向
- flask中通过redirect()函数实现重定向功能
- 配合url反向解析函数url_for()可以减少url重构的开发量
from flask import Flask,redirect,url_for
app=Flask(__name__)
@app.route('/demo1')
def demo1():
url_=url_for('demo2',userid=1)
return redirect(url_)
@app.route('/demo2/<userid>')
def demo2(userid):
print(userid)
return 'demo2'
if __name__ == '__main__':
app.run()
运行结果:
127.0.0.1 - - [17/Mar/2020 22:40:22] "GET /demo1 HTTP/1.1" 302 -
127.0.0.1 - - [17/Mar/2020 22:40:22] "GET /demo2/1 HTTP/1.1" 200 -
1
5)响应状态码
from flask import Flask, redirect, url_for
app = Flask(__name__)
@app.route('/demo5')
def demo5():
# 可以通过第二个返回值 来设置自定义的响应状态码 实现自定义的交互规则 700 密码错误 701 用户不存在
# 返回值: 响应体, 响应状态码, 响应头
return 'demo5', 700, {
'B': 40}
if __name__ == '__main__':
app.run(debug=True)
运行结果:
127.0.0.1 - - [17/Mar/2020 22:44:25] "GET /demo HTTP/1.1" 700 -
状态保持
1)cookie
- 特点
- 将数据保存在客户端,可以减轻服务器的压力
- 访问网站时,浏览器将自动发送cookie数据给服务器
- 一般用于保存一些不太重要的数据
- 设置cookie:response.set_cookie(键,值,max_age=最大生存时间)
- 获取cookie:ressponse.cookie.get(键)
- 删除cookie:response.delete_cookie(键),删除cookie的本质是将max_age设置为0
from flask import Flask ,make_response, Response, request
app =Flask(__name__)
@app.route('/demo1')
def demo1():
response= make_response()
response.set_cookie('name','zhu',max_age=600)
return response
@app.route('/demo2')
def demo2():
print(request.cookies.get('name'))
return 'demo2'
if __name__=="__main__":
app.run()
运行结果:
127.0.0.1 - - [17/Mar/2020 22:59:31] "GET /demo1 HTTP/1.1" 200 -
127.0.0.1 - - [17/Mar/2020 22:59:35] "GET /demo2 HTTP/1.1" 200 -
zhu
2)session
- 特点:将数据保存在服务端
- 一般用来保存一些重要或敏感的数据
- 设置应用密钥,用于session签名:app.secret_key =‘test’
- 设置session过期时间,默认31天:app.permanent_session_lifetime = timedelta(days=天数)
- 设置session:session[键]=值
- 设置session是否支持过期时间:session.permanent = True
- 删除session:session.pop(键)
- 获取session: session.get(键)
- flask的默认session机制 没有将session数据保存到服务器的数据库中, 而是将session数据编码后保存到了cookie中 (签名cookie机制)
- 可以使用 flask-session组件 来实现传统的session存储
from flask import Flask session
from datetime import timedelta
app=Flask(__name__)
app.secret_key ='test'
app.permanent_session_lifetime = timedelta(days=7)
@app.route('/demo1')
def demo1():
session['password']= '123'
session.permanent=True
return 'demo1'
@app.route('/demo2')
def demo2():
print(session.get('password'))
return 'demo2'
if __name__=='__main__':
app.run()
运行结果:
127.0.0.1 - - [17/Mar/2020 23:16:44] "GET /demo1 HTTP/1.1" 200 -
123
127.0.0.1 - - [17/Mar/2020 23:17:11] "GET /demo2 HTTP/1.1" 200 -
异常处理
- flask对http错误进行封装,可以捕获http错误,也可以主动抛出http错误
from flask import Flask ,abort
app = Flask(__name__)
@app.errorhandler(404)
def error_404(error):
return "<h3>您访问的页面去浪迹天涯了</h3> %s" % error
@app.errorhandler(ZeroDivisionError)
def error_zero(error):
return '除数不能为0'
@app.route('/') #访问其他路由时会捕获404异常,跳到error_404函数
def index():
a=1/0 #产生除数为0的异常,会主动跳到error_zero函数
#abort(500) abort()函数可以主动抛出异常
return 'index'
if __name__=='__main__':
app.run()
3.高级处理
请求钩子
- 请求钩子可以对请求的各阶段进行监听, 方便开发者 针对请求完成一些统一的处理, 以便减少重复代码, 作用类比Django中的中间件
- 一般使用以下四种钩子
- before_request
- 每次执行视图函数之前调用
- 对请求进行一些准备处理
- 如果在某修饰对的函数中返回了一个响应,视图函数将不再被调用
- after_request
- 如果没有抛出错误,每次执行视图函数之后调用
- 在此函数中可以对响应值在返回之前做最后的处理
- 接收一个参数:包装好的响应对象
- 需要将修改后的响应对象返回
- before_first_request
- web应用被第一次请求前调用
- 可以进行web应用初始化处理
- teardown_request
- 每次执行视图函数之后调用
- 无论是否出现异常都会执行,一般用于请求收尾
- 接收一个参数:错误新信息,如果有相关错误抛出
- before_request
from flask import Flask ,Response
app= Flask(__name__)
@app.before_request
def br():
print('before_request')
@app.after_request
def ar(response:Response):
print('after_request')
return response
@app.before_first_request
def bfr():
print('before_first_request')
@app.teardown_request
def tr(error):
print('reardown_request:%s'%error)
@app.route('/')
def index():
print('执行视图函数')
# a=1/0 执行此命令则不会调用before_request钩子
return 'index'
if __name__ =='__main__':
app.run()
运行结果:
before_first_request
before_request
执行视图函数
after_request
reardown_request:None
127.0.0.1 - - [18/Mar/2020 10:50:48] "GET / HTTP/1.1" 200 -
蓝图
1)基本使用
- 蓝图的作用:实现flask项目的模块化
- 项目模块化主要是将业务以功能模块进行划分,每个功能模块对应一个包,用于存放和其有关的视图/工具/模型文件等,如home,user
- 对于大型项目,一般每个功能模块对应创建一个蓝图,由多个蓝图代替应用来分别管理各模块的视图
--------- project # 工程目录
|------ main.py # 启动文件
|------ user # 用户模块
| |--- __init__.py # 包的初始化文件, 此处创建蓝图对象
| |--- views.py # 视图文件
| |--- ...
|
|------ home # 首页模块
| |--- __init__.py
| |--- views.py
| |--- ...
|...
代码示例
- home/init.py 在home包中的初始化文件__init__.py中创建蓝图
from flask import Blueprint
home_blu = Blueprint('home_b',__name__) #创建蓝图
# home_blu:蓝图对象,home_b:蓝图名
from . import views # 导入视图文件
- home/views.py 在views.py文件中存放视图
from home import home_blu
@home_blu.route('/') #路由不再通过app.route()创建,而是通过蓝图对象.route()来创建
def index():
return 'index'
- main.py 在项目的main.py文件中注册蓝图
from flask import Flask
from home import home_blu #导入蓝图
app = Flask(__name__)
app.register_blueprint(bome_blu) #注册蓝图
if __name__=='__main__':
print(app.url_map)
app.run()
运行结果:
Map([<Rule '/' (GET, OPTIONS, HEAD) -> home_b.index>,
<Rule '/static/<filename>' (GET, OPTIONS, HEAD) -> static>])
127.0.0.1 - - [18/Mar/2020 12:01:22] "GET / HTTP/1.1" 200 -
2)使用细节
- 蓝图的三个使用细节
- 创建蓝图时,可以通过url_prefix参数给蓝图定义的录用添加统一的url资源段前缀
- 蓝图定义的路由,其函数标记为蓝图名.函数名
- 蓝图也可以设置请求钩子
- 只有访问该蓝图定义的路由时才会触发
- 实现局部监听
代码示例
- home/init.py
from flask import Blueprint
home_blu = Blueprint('home_b',__name__,url_prefix='/home')
# url_prefix参数用来给url添加统一资源前缀
@home_blu.before_request #使用蓝图对象创建钩子
def home_prepare():
print('home_before_request')
from . import views
- home/views.py
from flask import url_for
from home import home_blu
@home_blu.route('/demo1') #路由/home/demo1
def demo1():
return 'demo1'
@home_blu.route('/demo2') #路由/home/demo2
def demo2()
url1=url_for('home_b.demo1')
print(url1)
print('demo2')
return 'demo2'
运行结果:
Map([<Rule '/home/demo1' (OPTIONS, HEAD, GET) -> home_b.demo1>,
<Rule '/home/demo2' (OPTIONS, HEAD, GET) -> home_b.demo2>,
<Rule '/static/<filename>' (OPTIONS, HEAD, GET) -> static>])
home_before_request
demo1
127.0.0.1 - - [18/Mar/2020 12:15:56] "GET /home/demo1 HTTP/1.1" 200 -
home_before_request
/home/demo1
demo2
127.0.0.1 - - [18/Mar/2020 12:16:01] "GET /home/demo2 HTTP/1.1" 200 -
上下文
- 上下文:是一个数据容器,保存了flask程序运行过程中的一些新信息
- flask中由两种上下文,请求上下文和应用上下文
- 请求上下文:记录一些和请求有关的数据,包括request和session两个变量
- request
- 封装了http请求的内容,针对的时http请求
- session
- 用来记录请求绘画中的信息,针对的是用户信息
- request
- 应用上下文:记录一些和应用有关的数据,包括current_app和g两个变量
- current_app
- 会自动引用创建的flask对象,需要在项目的其他温江中使用app时,应该通过current_app来获取,可以减少循环导入问题
- g
- flask给开发者预留的一个容器,用于记录自定义数据
- g变量每次请求会重制数据
- g使用场景
- 在钩子函数和视图函数之间传递数据
- 函数嵌套调用时传递数据
- current_app
- 请求上下文:记录一些和请求有关的数据,包括request和session两个变量
- 两种上下文的使用范围相同,从请求开始道请求结束,在范围外使用会报错
- 上下文之间线程隔离,不同于全局变量
- 上下文在请求开始时被创建,在请求结束时被销毁
代码示例
- demo.py
from flask import Flask,request,current_app,g
import tool
app=Flask(__name__)
@app.route('/')
def demo():
g.name='zhu'
tool.index()
return 'demo'
@app.route('/demo1')
def demo1():
print(g.name) #不在同一个请求中,会报错
return 'demo1'
if __name__=='__main__':
# print(request.url) #请求外,会报错
app.run()
- tool.py
from flask import Flask,request,current_app,g
def index():
print(g.name)
print(current_app.url_map)
运行结果:
zhu
Map