Flask框架
课程:Flask
# 一.flask介绍
# 1.1 学习Flask框架的原因
从全球开发者关注程度上:Django为5.8K,Flask为3.31K
从国外招聘网站岗位需求上:提到Django的有1.77K,Flask为942
从著名的问答网站stack overflow提问数量上:Django 196K,Flask为27.6K
从全球著名的代码托管平台上的star和fork数量上:Django分别为41.3K、17.8K,Flask分别为43.8K、12.3K
# 1.2 Flask vs Django
# Django
# flask
Flask 本身相当于一个内核,其他几乎所有的功能都要用到扩展(邮件扩展Flask-Mail,用户认证Flask-Login,数据库Flask-SQLAlchemy),都需要用第三方的扩展来实现。比如可以用 Flask 扩展加入ORM、窗体验证工具,文件上传、身份验证等。Flask 没有默认使用的数据库,你可以选择 MySQL,也可以用 NoSQL。
其 WSGI 工具箱采用 Werkzeug(路由模块),模板引擎则使用 Jinja2。这两个也是 Flask 框架的核心。
# 1.3 常用扩展包
扩展列表:http://flask.pocoo.org/extensions/
- 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.4 Flask文档
- 中文文档(http://docs.jinkan.org/docs/flask/)
- 英文文档(http://flask.pocoo.org/docs/1.0/)
# 二.工程搭建
# 2.1安装环境
我们的用flask版本是1.1.1,注意:联网的状态下下载
pip install [安装包] 安装安装包
pip uninstall [安装包] 卸载安装包
pip list 列出所有安装的依赖包
pip install flask==1.1.1 下载flask
pip install flask==1.1.1 -i https://pypi.doubanio.com/simple
2
3
4
5
# 2.2 hello world程序
# 2.2.1 目标
掌握flask的编写方式
# 2.2.2 代码
创建helloworld.py文件
#导入Flask类
from flask import Flask
# 创建应用app,Flask类接收参数__name__
app = Flask(__name__)
# 将路由映射到对应的视图函数index
@app.route('/')
def index():
return 'hello world'
# Flask 应用程序实例化的run方法启动web服务器
if __name__ == '__main__':
app.run()
2
3
4
5
6
7
8
9
10
11
12
13
# 2.2.3 运行程序
- 手动启动
python helloworld.py
- pycharm运行
和其他python文件的启动方式一样
# 2.3 参数说明
- Flask对象的初始化参数
- 应用程序配置参数
- app.run()运行参数
# 2.3.1 Flask 对象初始化参数
Flask 程序再实例化创建的时候,需要默认传入当前的Flask程序所指定的(模块),接下来就来详细查看一下Flask应用程序在创建的时候一些需要我们关注的参数
- import _name
- Flask程序所在的包(模块),传入
__name__
就可以 - 其可以决定flask在访问静态文件的时候查找的路径
- Flask程序所在的包(模块),传入
- static_folder
- 静态文件存储的文件夹,可以不传,默认为
static
- 静态文件存储的文件夹,可以不传,默认为
- template_folder
- 模板文件存储的文件夹,可以不传,默认为
templates
- 模板文件存储的文件夹,可以不传,默认为
# 2.3.1.1默认参数情况下
app = Flask(__name__)
文件目录
----
|---static
|---templates
| |--- 1.png
|---helloworld.py
2
3
4
5
6
访问
127.0.0.1:5000/static/1.png
就可以访问到图片
# 2.3.1.2修改参数的情况下
app = Flask(__name__,static_folder='folder_param')
文件目录
----
|---folder_param # 此处目录名变化
| |--- 1.png
|---helloworld.py
2
3
4
# 2.3.2 应用程序配置参数
对于Flask对象初始化参数仅仅设置的是Flask本身的属性,比如:
- Flask从哪里读取静态文件
- Flask从哪里读取模板文件
- ...
等等。
应用程序配置参数设置的是一个Web应用工程的相关信息,比如:
- 数据库的连接信息
- 日志的配置信息
- 自定义的配置信息
- ...
等等
# 2.3.2.1作用
集中管理项目的所有配置信息
# 2.3.2.2使用方式
Flask将配置信息保存到了app.config
属性中,该属性可以按照字典类型进行操作。
2.3.2.3读取
app.config.get(name)
app.config[name]
2
2.3.2.4设置
主要有以下三种方式
- 从配置对象中加载
app.config.from_object(配置对象)
class DefaultConfig(object):
'''默认配置'''
IP = '11111111' # 常量 必须大写
app = Flask(__name__)
# 把配置对象加入到app的管理配置文件中
app.config.from_object(DefaultConfig)
# 将路由映射到对应的视图函数index
@app.route('/')
def index():
print(app.config.get('IP'))
return 'hello world'
2
3
4
5
6
7
8
9
10
11
12
13
应用场景:
作为默认配置写在程序代码中
可以继承
class DevelopConfig(DefaultConfig):
DEBUG =True
2
- 从配置文件中加载
app.config.from_pyfile(配置文件)
新建一个配置文件setting.py
SECRET_KEY = '44444444'
在Flask程序文件中
app = Flask(__name__)
app.config.from_pyfile('setting.py')
@app.route("/")
def index():
print(apyp.config['SECRET_KEY'])
return "hello world"
2
3
4
5
6
7
8
应用场景:
在项目中使用固定的配置文件
- 从环境变量中加载
环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等。 环境变量是在操作系统中一个具有特定名字的对象,它包含了一个或者多个应用程序所将使用到的信息。
通俗的理解,环境变量就是我们设置在操作系统中,由操作系统代为保存的变量值
在pycharm运行时设置环境变量的方式
Flask使用环境变量加载配置的本质是通过环境变量值找到配置文件,再读取配置文件的信息,其使用方式为
app.config.from_envvar('环境变量名')
环境变量的值为配置文件的绝对路径
现在pycharm中配置环境变量
再运行如下代码
app = Flask(__name__)
app.config.from_envvar('testfan', silent=True)
@app.route("/")
def index():
print(app.config['SECRET_KEY'])
return "hello world"
2
3
4
5
6
7
8
关于silent
的说明:
表示系统环境变量中没有设置相应值时是否抛出异常
- False 表示不安静的处理,没有值时报错通知,默认为False
- True 表示安静的处理,即时没有值也让Flask正常的运行下去
应用场景:
配置文件的地址不固定;
在代码中不想暴露真实的配置文件地址,只在运行代码的服务器上才有真实配置文件的信息。
# 项目中的常用方式
创建定制化的Flask app,并结合使用配置对象与环境变量加载配置
- 使用配置对象加载默认配置
- 使用环境变量加载不想出现在代码中的敏感配置信息
def create_app(config):
app = Flask(__name__)
app.config.from_object(config)
app.config.from_envvar('TESTFAN')
return app
class DefaultConfig(object):
'''默认配置'''
IP = '12222222' # 必须大写
class DevelopConfig(DefaultConfig):
DEBUG = True
app = create_app(DefaultConfig)
# app = Flask(__name__)
# app.config.from_object(DevelopConfig)
# 将路由映射到对应的视图函数index
@app.route('/')
def index():
print(app.config['SECRET_KEY'])
print(app.config['IP'])
return 'hello world'
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 2.3.3 app.run参数
可以指定运行的主机IP地址,端口,是否开启调试模式
app.run(host="0.0.0.0", port=5000, debug = True)
关于DEBUG调试模式
- 程序代码修改后可以自动重启服务器
- 在服务器出现相关错误的时候可以直接将错误信息返回到前端进行展示
# 三.路由与蓝图
# 3.1 路由
指定请求方式
在 Flask 中,定义路由其默认的请求方式为:
- GET
- OPTIONS(自带)
- HEAD(自带)
利用methods
参数可以自己指定一个接口的请求方式
@app.route('/demo1/')
def demo1():
return '我是get请求'
@app.route('/demo2/',methods=['GET','POST'])
def demo2():
return '我既可以用get,又可以用post'
2
3
4
5
6
7
# 3.2 蓝图
3.2.1需求
在一个Flask 应用项目中,如果业务视图过多,可否将以某种方式划分出的业务单元单独维护,将每个单元用到的视图、静态文件、模板文件等独立分开?
例如从业务角度上,可将整个应用划分为用户模块单元、商品模块单元、订单模块单元,如何分别开发这些不同单元,并最终整合到一个项目应用中?
3.2.2蓝图
在Flask中,使用蓝图Blueprint来分模块组织管理。
蓝图实际可以理解为是一个存储一组视图方法的容器对象,其具有如下特点:
- 一个应用可以具有多个Blueprint
- 可以将一个Blueprint注册到任何一个未使用的URL下比如 “/user”、“/goods”
- Blueprint可以单独具有自己的模板、静态文件或者其它的通用操作方法,它并不是必须要实现应用的视图和函数的
- 在一个应用初始化时,就应该要注册需要使用的Blueprint
但是一个Blueprint并不是一个完整的应用,它不能独立于应用运行,而必须要注册到某一个应用中。
3.2.3使用方式
使用蓝图分为三个步骤
1.创建一个蓝图对象
user_bp= Blueprint('user',__name__)
2.把蓝图对象绑定静态文件和模板文件,注册路由,映射到对应的视图
@user_bp.route('/')
def user_profile():
return 'user_profile'
2
3
3.在应用对象上面注册这个蓝图对象
app.register_blueprint(user_bp)
- 单文件蓝图
可以将创建蓝图对象与定义视图放到一个python文件中
- 目录(包)蓝图
对于一个打算包含多个文件的蓝图,通常将创建蓝图对象放到python包的__init__
文件中
--------- project # 工程目录
|------ main.py # 启动文件
|------ user #用户蓝图
| |--- __init__.py # 此处创建蓝图对象
| |--- passport.py
| |--- profile.py
| |--- ...
|
|------ goods # 商品蓝图
| |--- __init__.py
| |--- ...
|...
2
3
4
5
6
7
8
9
10
11
12
参数扩展
- 指定url的前缀
在应用中注册蓝图时使用
url_prefix
参数指定 或者在定义蓝图的时候使用url_prefix
app.register_blueprint(user_bp,url_prefix='/user')8 user_bp = Blueprint('user',__name__,url_prefix='/user')
1
2- 蓝图内部静态文件
和应用对象不同,蓝图对象创建时不会默认注册静态目录的路由。需要我们在 创建时指定 static_folder 参数。
下面的示例将蓝图所在目录下的static_admin目录设置为静态目
admin = Blueprint("admin",__name__,static_folder='static_admin') app.register_blueprint(admin,url_prefix='/admin')
1
2- 蓝图内部模板文件
蓝图对象默认的模板目录为系统的模版目录,可以在创建蓝图对象时使用 template_folder 关键字参数设置模板目录
admin = Blueprint("admin",__name__,template_folder='my_templates') app.register_blueprint(admin,url_prefix='/admin')
1
2# 四.请求与响应
# 4.1处理请求
4.1.1需求
在视图编写中需要读取客户端请求携带的数据时,如何才能正确的取出数据呢?
请求携带的数据可能出现在HTTP报文中的不同位置,需要使用不同的方法来获取参数。
4.1.2url路径参数(动态路由)
例如,有一个请求访问的接口地址为
/users/123
,其中123实际上为具体的请求参数,表明请求123号用户的信息。此时如何从url中提取出123的数据? flask采用转换器的形式@app.route('/users/<user_id>') def user_info(user_id): print(type(user_id)) return 'hello user {}'.format(user_id)
1
2
3
4此处的<>即是一个转换器,默认为字符串,即将该位置的数据以字符串格式进行匹配,并以字符串为数据类型,
user_id
为参数名传入视图将上面的例子以整型匹配数据,可以如下使用
@app.route('/users/<int:user_id>') def user_info(user_id): print(type(user_id)) return 'hello user {}'.format(user_id)
1
2
3
44.1.3 其他参数
如果想要获取其他地方传递的参数,可以通过Flask提供的request对象来读取。
不同位置的参数都存放在request的不同属性中
属性 说明 类型 data 记录请求的数据,并转换为字符串 * form 记录请求中的表单数据 MultiDict args 记录请求中的查询参数 MultiDict cookies 记录请求中的cookie信息 Dict headers 记录请求中的报文头 EnvironHeaders method 记录请求使用的HTTP方法 GET/POST url 记录请求的URL地址 string files 记录请求上传的文件 * 例如 想要获取请求
/articles?channel_id=1
中channel_id
的参数,可以按如下方式使用:from flask import request @app.route('/articles') def get_articles(): channel_id = request.args.get('channel_id') return 'you wanna get articles of channel {}'.format(channel_id)
1
2
3
4
5
6上传文件
postman模拟客户端上传文件
后台代码如下:
# 定义一个函数可以把前端发送过来的图片保存到服务器 @app.route('/upload',methods=['POST']) def upload(): #已经用postman模拟前端请求,获取图片数据 upload_file=request.files.get('pic')# 存在服务器内存中,关机就没了,只是暂时保存 print(type(upload_file))#------------返回的是<class 'werkzeug.datastructures.FileStorage'> #upload_file是文件类型,要想要图片,要用read读取 # with open('./demo.png','wb') as new_file: # new_file.write(upload_file.read()) # # 每一个视图函数都要有一个return返回值 #upload_file不是一般普通的文件类型,flask返回的这个比较特殊,有一个save()方法,可以直接保存,功能类似with...open那个 upload_file.save() #这个才是永远保存到磁盘或第三方或数据库(存储位置不一样) return 'save ok'
1
2
3
4
5
6
7
8
9
10
11
12
13# 4.2处理响应
需求:
如何在不同的场景里返回不同的响应信息?
1.返回模板
使用
render_template
方法渲染模板并返回例如,新建一个模板index.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>testfan</title> </head> <body> <p>欢迎来到testfan学习</p> </body> </html>
1
2
3
4
5
6
7
8
9
10后端视图
from flask import render_template @app.route('/demo1/') def demo1(): return render_template('index.html')
1
2
3
42.重定向
from flask import redirect @app.route('/demo3/') def demo3(): # return redirect('http://www.testfan.cn') return redirect(url_for('demo2'))
1
2
3
4
53.返回json
from flask import jsonify @app.route('/demo2/') def demo2(): return jsonify({ 'status':200, 'message':'ok' })
1
2
3
4
5
6
74.自定义状态码和响应头
1)元组的方式
可以返回一个元组,这样的元组必须是(response,status,headers)的形式,且至少包含一个元素
status值会覆盖状态码,headers可以是一个列表或字典,作为额外的消息标头值
@app.route('/demo4/') def demo4(): # return '我是一个response',888,{'testfan':'good'} # return '我是一个response' return '我是一个response',888,[('testfan','good'),('python','good')]
1
2
3
4
52)make_response方式
from flask import make_response @app.route('/demo5/') def demo5(): resp = make_response('这是自己构造的响应') resp.headers['testfan']='good' return resp
1
2
3
4
5
6# 4.3cookie与session
4.3.1 Cookie
设置
@app.route('/demo6/')
def demo6():
resp = make_response('这是自己构造的函数')
resp.set_cookie('testfan','yaoyao')
return resp
2
3
4
5
设置有效期
@app.route('/demo6/')
def demo6():
resp = make_response('这是自己构造的函数')
resp.set_cookie('testfan','yaoyao',max_age=3600)
return resp
2
3
4
5
- 读取
@app.route('/demo6/')
def demo6():
resp = make_response('这是自己构造的函数')
s = request.cookies.get('testfan')
print(s)
return resp
2
3
4
5
6
删除
删除的意思其实就是让cookie有效期提前到期,并不能真的删除cookie
@app.route('/demo6/')
def demo6():
resp = make_response('这是自己构造的函数')
resp.delete_cookie('testfan')
return resp
2
3
4
5
4.3.2 Session
需要先设置SECRET_KEY
class DefaultConfig(object):
SECRET_KEY = '11111111'
app.config.from_object(DefaultConfig)
或者直接设置
app.secret_key='111111111'
2
3
4
5
6
7
设置
@app.route('/set_session/')
def set_session():
session['username']= 'testfan'
return 'set_session ok'
2
3
4
读取
@app.route('/get_session/')
def get_session():
username = session.get('username')
return '欢迎{}'.format(username)
2
3
4
# 4.4 异常处理
http异常主动抛出
- abort方法
- 抛出一个给定状态代码的HTTPException或者指定响应,例如想要用一个页面未找到异常来终止请求,你可以调用abort(404)
- 参数
- code-HTTP的错误状态码
abort(500)
抛出状态码的话,只能抛出HTTP协议的错误状态码
捕获错误
- errorhandler 装饰器
- 注册一个错误处理程序,当程序抛出指定错误状态码的时候,就会调用该装饰器所装饰的方法
- 参数
- code_or_exception-HTTP的错误状态码或指定异常
- 例如统一处理状态码为500的错误给用户友好的提示
@app.errorhandler(404)
def errorpage(e):
return '我出去度假了,勿念'
2
3