Flask
Flask是一个使用 Python 编写的轻量级 Web 应用框架。其 WSGI 工具箱采用 Werkzeug ,模板引擎则使用 Jinja2 。
django和flask的区别
django是个大而全的框架,flask是一个轻量级的框架。django内部为我们提供了非常多的组件,flask框架本身没有太多的功能但是第三方组件非常齐全。 django的请求处理是逐一封装和传递,flask的请求是利用上下文管理来实现的。
安装
pip3 install flask
Werkzeug
flask依赖wsgi是Werkzeug
from werkzeug.serving import run_simple
from werkzeug.wrappers import BaseResponse
def func(environ, start_response):
response = BaseResponse("hello")
return response(environ, start_response)
if __name__ == '__main__':
run_simple('127.0.0.1', 5000, func)
模拟Flask
from werkzeug.serving import run_simple
class Flask(object):
def __call__(self,environ, start_response):
pass
def run(self):
run_simple('127.0.0.1', 5000, self)
app = Flask()
if __name__ == '__main__':
app.run()
使用Flask
from flask import Flask
# 导入Flask
app = Flask(__name__)
# 创建Flask对象
@app.route("/index")
# 建立路由和函数的对应关系
def index():
return "index"
if __name__ == "__main__":
app.run()
# 启动
路由
路由的两种写法
# 方式一:
@app.route('/index',methods=['GET','POST'],endpoint="login")
# methods指定允许的请求
# endpoint类似django中的别名
def login():
pass
# 方式二:
def index():
return render_template('index.html')
app.add_url_rule('/index', 'index', index)
# app.add_url_rule('/路径', '函数名', 函数)
动态路由
@app.route('/index/<int:nid>')
# <int:nid> 接收一个url值,默认为字符串类型,可以通过int:nid这种方式指定类型
def login(nid):
print(nid)
支持正则表达式的路由
from flask import Flask,render_template
app = Flask(__name__)
from werkzeug.routing import BaseConverter
class RegConverter(BaseConverter):
# 必须继承BaseConverter类
def __init__(self, map, regex):
super().__init__(map)
self.regex = regex
app.url_map.converters['regex'] = RegConverter
@app.route('/index/<regex("\d+"):xx>')
def index(xx):
return render_template('index.html')
if __name__ == '__main__':
app.run()
视图
FBV
def index():
return render_template('index.html')
app.add_url_rule('/index', 'index', index)
@app.route('/login')
def login():
return render_template('login.html')
CBV
from flask import Flask,render_template,views
app = Flask(__name__,)
def t1(func):
def inner(*args,**kwargs):
result = func(*args,**kwargs)
return result
return inner
class Index(views.MethodView):
methods = ['GET',"POST"]
# methods :指定允许的请求
decorators = [t1,]
# 添加装饰器
def get(self):
return 'get'
def post(self):
return 'post'
app.add_url_rule('/index', view_func=Index.as_view('user'))
# app.add_url_rule('url', view_func=类.as_view('endpoint'))
if __name__ == '__main__':
app.run()
获取数据
flask和django一样,都是从request中获取数据,但是flask的request需要导入
from flask import Flask,request
app = Flask(__name__)
@app.route("/index",methods=["GET","POST"])
def index():
print(request.args)
print(request.args.get("page"))
# 获取url中的数据
print(request.form)
print(request.form.get("name"))
# 获取form提交的数据
return "index"
if __name__ == "__main__":
app.run()
返回值
在flask中不仅可以返回页面、json数据等还可以返回一个函数
返回html文件
html文件默认需要放在templates目录下,如果要指定其他目录可以通过template_folder属性指定
from flask import Flask,request,render_template,redirect,jsonify
app = Flask(__name__,template_folder="htmls")
# template_folder指定存放html的文件夹
@app.route("/index")
def index():
return render_template("index.html")
# 返回页面
return render_template("index.html",name="zxc")
# 携带值返回页面
return render_template("index.html",**{name:"zxc"})
# 值以字典形式返回
if __name__ == "__main__":
app.run()
重定向
需要导入redirect
from flask import Flask,request,render_template,redirect,jsonify,url_for
app = Flask(__name__)
@app.route("/index",methods=["GET","POST"])
def index():
return redirect("/login")
# 通过请求路径重定向
return redirect(url_for("log"))
# 通过别名重定向
if __name__ == "__main__":
app.run()
@app.route("/login",endpoint="log")
def login():
return "login"
页面使用url_for
{{ url_for('蓝图名.别名' ,参数=值)}}
返回json数据
需要导入jsonify
from flask import Flask,request,render_template,redirect,jsonify
app = Flask(__name__)
@app.route("/index",methods=["GET","POST"])
def index():
return jsonify([{"name":"1","age":"2"}])
if __name__ == "__main__":
app.run()
模板
flask的模板使用方法和django类似
语法
# 取值
{{ name }}
# 循环
{% for item in obj%}
{{item}}
{{item.name}}
{{item["name"]}}
{% endfor %}
# 使用items时需要手动添加括号
{% for k,v in names.items()%}
{% endfor %}
模板继承
创建模板
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1>1</h1>
{% block content %}
{% endblock %}
</body>
</html>
导入模板
{% extends 'base.html' %}
{% block content %}
<h1>2</h1>
{% endblock %}
组件
将一个页面作为组件加载到另一个页面中
{% include '2.html' %}
全局模板
定义在蓝图中时只能在本蓝图中使用
from flask import Flask,render_template,views
app = Flask(__name__,)
@app.template_global()
# 类似django中的自定义标签
def func(arg):
# arg:接受参数
return 'str1' + arg
@app.template_filter()
# 类似django中的自定义筛选器
def x1(s1,s2):
return 'str3' + s1 + s2
@app.route("/index")
def index():
return render_template("index.html")
if __name__ == '__main__':
app.run()
<body>
{{ func("str2") }}
{{ "str4"|func2("str5") }}
</body>
静态文件
flask中默认静态文件要放在static目录中,可以通过static_folder指定存放静态文件的目录
from flask import Flask,render_template
app = Flask(__name__,static_folder="statics",static_url_path="/pac")
# static_folderL:指定存放静态文件的目录
# static_url_path:指定页面上静态文件的“路径“,默认为static
@app.route("/index")
def index():
return render_template("index.html")
<body>
<!--方式一:固定写法,static_url_path改变路径不会变-->
<img src="/pic/1.jpg" />
<!--/pic=static_url_path设定的值-->
<!--方式二:动态写法,随着static_url_path改变-->
<img src="{{ url_for('static',filename='1.jpg' )}}" />
<!--包含子路径的写法-->
<script src="{{ url_for('static',filename='js/bootstrap.min.js' )}}"></script>
</body>
配置文件
全局变量实现:
# /config/settings.py
NAME = "XZC"
try:
from .localsettings import *
# 导入另一个文件中的全部配置
except ImportError:
pass
from flask import Flask
app = Flask(__name__)
app.config.from_object("config.settings")
# 加载配置
@app.route("/index")
def index():
print(app.config["NAME"])
# 读取配置文件
return "hello"
基于类实现:
# /config/settings.py
class BaseSettings:
"""公共配置"""
class DevSettings(BaseSettings):
"""私有配置"""
NAME = "zxc"
from flask import Flask
app = Flask(__name__)
app.config.from_object("config.settings.DevSettings")
# 导入settings
@app.route("/index")
def index():
print(app.config["NAME"])
return "hello"
if __name__ == '__main__':
app.run()
session
django中的session和flask中的session有一些不同,django中的session是存放在服务器上的,而flask的session是存放在浏览器上的。使用session需要设置secret_key 值随意,服务器会对session数据和secret_key 进行加密,然后将加密后的字符串返回给浏览器,保存在浏览器的cookie中。当浏览器携带这个密文访问时,flask会自动将密文进行解密。
from flask import Flask,request,session
app = Flask(__name__,template_folder="a")
app.secret_key = "dhuaicnjk"
# 设置secret_key
@app.route("/index",methods=["GET","POST"])
def index():
if request.method == "GET":
session["name"] = "zxc"
# 设置session
return "index"
name = session.get("name")
# 取出session
return name
if __name__ == "__main__":
app.run()
装饰器
flask使用装饰器时要放在@app.route()下,装饰器中必须使用functools
from flask import Flask,request,session,render_template
import functools
app = Flask(__name__)
app.secret_key = "dhuaicnjk"
def auth(func):
# 需要使用functools保证被装饰的函数的名称不变
@functools.wraps(func)
def inner(*args,**kwargs):
return func(*args,**kwargs)
return inner
@app.route("/index")
@auth
def index():
return "index"
if __name__ == "__main__":
app.run()
蓝图
flask中的蓝图类似于django的app,和djasngo的app不同的是,django中需要在配置文件中注册app,而flask的蓝图是通过导入来使用
创建蓝图:
#views.bp1.py
from flask import Blueprint
# 导入Blueprint
bp1 = Blueprint('bp1',__name__)
# 创建蓝图
@bp1.route('/index')
def index():
return 'index'
导入蓝图:
from flask import Flask
from views.bp1 import bp1
# 导入蓝图
app = Flask(__name__)
app.register_blueprint(bp1)
# 使用蓝图
# app.register_blueprint(bp1,url_prefix='/web')
# url_prefix类似django的路由分发
if __name__ == '__main__':
app.run()
基于蓝图的两种项目目录形式
方式一
# manage.py
from flask1 import create_app
app = create_app()
if __name__ == '__main__':
app.run()
# __init__.py
from flask import Flask
from .views.login import log
def create_app():
app = Flask(__name__)
app.secret_key = 'asdfaskdfjsd'
@app.route('/index')
def index():
return 'index'
app.register_blueprint(log)
return app
# login.py
from flask import Blueprint
log = Blueprint('log',__name__)
@log.route('/login')
def login():
return 'login'
方式二
# manage.py
from flask1 import create_app
app = create_app()
if __name__ == '__main__':
app.run()
# /flask/__init__.py
from flask import Flask
def create_app():
app = Flask(__name__)
app.config.from_object('config.settings')
from .index import index
app.register_blueprint(index)
return app
# /flask/index/__init__.py
from flask import Blueprint
index = Blueprint('index',__name__)
# /flask/index/views/index.py
from .. import index
from flask import render_template
@index.route('/login')
def login():
return render_template('index.html')
特殊装饰器
类似与django中的中间件,定义在蓝图中时,只作用于本蓝图
from flask import Flask,render_template,request
app = Flask(__name__)
@app.before_request
def f1():
# 不返回或返回空执行下一个before_request
# 如果返回了东西,那么返回了什么页面就会展示什么,并不再继续执行
@app.after_request
def f2(response):
# response参数必须有
return response
@app.route('/index')
def index():
return render_template('index.html')
if __name__ == '__main__':
app.run()
g
在一次请求请求的周期,可以在g中设置值,在本次的请求周期中都可以读取或复制。
相当于是一次请求周期的全局变量。
from flask import Flask,g
app = Flask(__name__)
@app.before_request
def f1():
g.x1 = 123
@app.route('/index')
def index():
print(g.x1)
return 'hello world'
if __name__ == '__main__':
app.run()
信号
信号,是在flask框架中为我们预留的钩子,让我们可以进行一些自定义操作。
安装:
pip install blinker
使用
1.中间件
from flask import Flask,render_template
app = Flask(__name__)
@app.route("/index")
def index():
return "index"
class MyMiddleware():
def __init__(self,old_app):
self.wsgi_app = old_app.wsgi_app
def __call__(self, *args, **kwargs):
# 在此进行操作
return self.wsgi_app(*args, **kwargs)
app.wsgi_app = MyMiddleware(app)
if __name__ == '__main__':
app.run()
2appcontext_pushed
当app_ctx被push到local中栈之后,会触发appcontext_pushed信号,之前注册在这个信号中的
方法,就会被执行。
from flask import Flask,render_template
from flask import signals
app = Flask(__name__)
@ signals.appcontext_pushed.connect
def func(arg):
print("appcontext_pushed被触发",arg)
@app.route("/index")
def index():
return "index"
if __name__ == '__main__':
app.run()
3.before_first_request
from flask import Flask,render_template
app = Flask(__name__)
@app.before_first_request
def func():
print("before_first_request被触发")
@app.route("/index")
def index():
return "index"
if __name__ == '__main__':
app.run()
4.request_started
from flask import Flask,render_template
from flask import signals
app = Flask(__name__)
@signals.request_started.connect
def func(arg):
print("request_started被触发",arg)
@app.route("/index")
def index():
return "index"
if __name__ == '__main__':
app.run()
5.url_value_processor
from flask import Flask,render_template
app = Flask(__name__)
@app.url_value_preprocessor
def func(endpoint,view_args):
print("url_value_preprocessors被触发")
@app.route("/index")
def index():
return "index"
if __name__ == '__main__':
app.run()
6.before_reuqest
from flask import Flask,render_template
app = Flask(__name__)
@app.before_request
def func():
print("before_request被触发")
@app.route("/index")
def index():
return "index"
if __name__ == '__main__':
app.run()
视图函数
7.before_render_template,template_rendered
from flask import Flask,render_template
from flask import signals
app = Flask(__name__)
@signals.before_render_template.connect
def func(app, template, context):
print("before_render_template被触发")
@signals.template_rendered.connect
def func2(app, template, context):
print("template_rendered被触发")
@app.route("/index")
def index():
return render_template("index.html")
if __name__ == '__main__':
app.run()
8.after_request
from flask import Flask,render_template
app = Flask(__name__)
@app.after_request
def func(response):
print("after_request被触发")
return response
@app.route("/index")
def index():
return "index"
if __name__ == '__main__':
app.run()
9.request_finished
from flask import Flask,render_template
from flask import signals
app = Flask(__name__)
@signals.request_finished.connect
def func(app,response):
print("request_finished被触发")
@app.route("/index")
def index():
return "index"
if __name__ == '__main__':
app.run()
10.got_request_exception
报错执行
from flask import Flask,render_template
from flask import signals
app = Flask(__name__)
@signals.got_request_exception.connect
def func(app,exception):
print("got_request_exception被触发")
@app.route("/index")
def index():
return "index"
if __name__ == '__main__':
app.run()
11.teardown_request
from flask import Flask,render_template
app = Flask(__name__)
@app.teardown_request
def func(exc):
print("do_teardown_request 被触发")
@app.route("/index")
def index():
return "index"
if __name__ == '__main__':
app.run()
12.request_tearing_down
from flask import Flask,render_template
from flask import signals
app = Flask(__name__)
@signals.request_tearing_down.connect
def func(app,exc):
print("request_tearing_down 被触发")
@app.route("/index")
def index():
return "index"
if __name__ == '__main__':
app.run()
13.appcontext_popped
from flask import Flask,render_template
from flask import signals
app = Flask(__name__)
@signals.appcontext_popped.connect
def func(app):
print("appcontext_popped 被触发")
@app.route("/index")
def index():
return "index"
if __name__ == '__main__':
app.run()
Flask源码
流程
准备阶段:
- 实例化Flask对象
- 加载配置文件(给app的config进行赋值)
- 添加路由映射
- 运行flask,当有用户请求,执行app.__call__方法。
请求到来:
- 创建ctx = RequestContext对象,其内部封装了 Request对象和session数据。
- 创建app_ctx = AppContext对象,其内部封装了App和g。
- 执行ctx.push触发将 ctx 和 app_ctx 分别通过自己的LocalStack对象将其放入到Local中,Local的
本质是以线程ID为key,以{“stack”:[]}为value的字典 - 执行所有的before_request函数
- 执行视图函数
- 执行所有after_request函数(session加密放到cookie中)
- 销毁ctx和app_ctx
flask-script
flask的组件,用于运行flask程序。
安装
pip3 install flask-script