Flask
Flask
一、介绍和安装
Flask是一个基于WSGI协议的轻量级web框架,它使用起来非常简单且快捷,并且有能力扩展到开发大型项目。它基于 Werkzeug and Jinja 开发,已经成为目前流行的Python web应用程序框架之一。Flask为开发者提供了一些建议,但是并不会强制依赖某些布局,由开发人员自己来选择他们想要使用的工具和库。在社区提供了许多Flask的扩展包,这使添加新功能变得容易。
参考文档:官方文档,中文文档
参考文章:https://www.cnblogs.com/wupeiqi/articles/7552008.html
源码:GitHub
安装:
pip install flask
二、基本使用
1 flask初识
先创建一个最简单的Flask应用
# 导入Flask类
from flask import Flask
# 创建一个该类的对象。第一个参数是应用模块或者包的名称。
# __name__ 是一个适用于大多数情况的快捷方式。有了这个参数, Flask 才能知道在哪里可以找到模板和静态文件等东西。
app = Flask(__name__)
# 然后我们使用route()装饰器来告诉 Flask 触发函数的URL,这里是根路径
@app.route("/")
def hello_world():
# 函数返回值就是浏览器中显示的信息。
# 默认的内容类型是HTML,因此字符串中的HTML代码会被浏览器渲染。
return "<p>Hello, World!</p>"
# 这里写在文件里启动,(还可以命令行启动)
app.run()
这样就启动了一个非常简单的服务器,默认flask会监听本地的5000端口,打开浏览器输入http://127.0.0.1:5000/即可看到hello world
。接下来对比django,看看flask中如何使用。
2 三种类型的响应
在django中,有render,HttpResponse,redirect
三种类型的响应,在flask中同样也有
from flask import Flask,render_template,redirect
app = Flask(__name__)
# 1.返回字符串就,对应django的HTTPResponse
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
# 2.返回模版文件render_template,对应django的render
@app.route("/")
def hello_world():
# 这里的index.html模版文件默认路径是根路径下的templates目录下(是Flask类的init方法中默认值,可以另行指定)
return render_template("index.html")
# 3.redirect,对应于django的redirect
@app.route("/")
def hello_world():
return redirect("https://www.baidu.com")
3 模版语法
flask中同样有模版语法,django的模版语法是自己写的,flask使用的是jinja模块,它更好用一些。在HTML中双大括号中的变量可以被动态替换,在点.
调用的同时,jinja还可以直接调用方法,并且可以括号取值、get取值。使用和django类似,
3.1 举例
demo.py
from flask import Flask,render_template,request,redirect
app = Flask(__name__)
USERS = {
1:{'name':'令狐冲','age':20,'gender':'男','text':"独孤九剑"},
2:{'name':'小龙女','age':18,'gender':'女','text':"玉女心经"},
3:{'name':'段誉','age':23,'gender':'男','text':"凌波微步"},
}
@app.route('/index',methods=['GET'])
def index():
return render_template('index.html',user_dict=USERS) # 可以直接在这里加自定义的键值
index.html
<body>
<h1>列表</h1>
<table>
<!--循环结构,与django不同的是,可以直接加括号调用方法-->
{% for k,v in user_dict.items() %}
<tr>
<!--对于字典内的数据,可以`.`取值,也可以中括号`[]`,还可以使用`get`方法-->
<td>{{k}}</td>
<td>{{v.name}}</td>
<td>{{v['age']}}</td>
<td>{{v.get('gender')}}</td>
<!--if判断-->
{% if k==2 %}
<td>秘笈!{{ secret }}</td>
{% endif %}
</tr>
{% endfor %}
</table>
</body>
3.2 总结:
变量
{{ 变量名 }}
循环
{% for i in var %}
...
{% endfor %}
条件
{% if ... %}
..
{% endif %}
4 请求方法、request对象、反向解析
4.1 举例
demo.py
from flask import Flask,render_template,request
app = Flask(__name__)
@app.route('/login',methods=['GET','POST'],endpoint='log') # endpoint路由的别名,用作反向解析
def login():
# request.method判断请求类型
if request.method == "GET":
params = request.query_string # get请求携带的数据从query_string取出,默认是字节byte类型
print(params)
return render_template("login.html")
else:
print(request.form) # form表单提交的post请求数据从form取出
user = request.form.get('user') # 可以使用get取值
pwd = request.form.get('pwd')
if user == 'abc' and pwd == '123':
return "post"
login.html
<body>
<h1>用户登录</h1>
<form method="post">
<input type="text" name="user">
<input type="text" name="pwd">
<input type="submit" value="登录">
</form>
</body>
4.2 总结
使用methods
参数,传递一个列表,指定接收什么类型的请求,比如GET,POST
。
endpoint
用作反向解析的关键字(别名)。使用url_for
作后端反向解析,根据endpoint
设置的值获取url。
request
对象导入即可使用,能够自动区别每次的请求对象,通过request.method
判断请求类型,
request.query_string
获取get请求携带参数,request.form.get
获取form表单提交的post请求数据。
5 路由的另一种形式
flask通过app.route
装饰器来实现路由分发,这是它的源码
def route(self, rule: str, **options: t.Any) -> t.Callable:
def decorator(f: t.Callable) -> t.Callable:
endpoint = options.pop("endpoint", None)
self.add_url_rule(rule, endpoint, f, **options)
return f
return decorator
举例:@app.route('/login',methods=['GET','POST'],endpoint='log')
,首先调用route函数返回装饰器,然后将视图函数作为参数传到decorator
中,然后调用self.add_url_rule
方法,此时的self是Flask类的对象(app对象),即Flask.add_url_rule
方法。所以,可以不使用装饰器,而改为类似django的路由分发形式:
from flask import Flask,render_template,redirect
app = Flask(__name__)
def hello_world():
return "hello!"
# 手动调用add_url_rule方法,把url地址、视图函数、反向解析别名传入
app.add_url_rule("/",view_func=hello_world,endpoint="root")
app.run()
访问 http://127.0.0.1:5000/同样生效。这和django中的url('/',xxxx.view,name="root")
的形式很相似。
6 配置文件与配置方法
flask中的配置文件是一个flask.config.Config
,它继承字典,可以像字典一样操作。默认配置为:
{
'DEBUG': get_debug_flag(default=False), # 是否开启Debug模式
'TESTING': False, # 是否开启测试模式
'PROPAGATE_EXCEPTIONS': None,
'PRESERVE_CONTEXT_ON_EXCEPTION': None,
'SECRET_KEY': None, # 密钥
'PERMANENT_SESSION_LIFETIME': timedelta(days=31),
'USE_X_SENDFILE': False,
'LOGGER_NAME': None,
'LOGGER_HANDLER_POLICY': 'always',
'SERVER_NAME': None,
'APPLICATION_ROOT': None,
'SESSION_COOKIE_NAME': 'session',
'SESSION_COOKIE_DOMAIN': None,
'SESSION_COOKIE_PATH': None,
'SESSION_COOKIE_HTTPONLY': True,
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': False,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
'PREFERRED_URL_SCHEME': 'http',
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': True,
'JSONIFY_MIMETYPE': 'application/json',
'TEMPLATES_AUTO_RELOAD': None,
}
6.1 配置方法1
直接写在项目里,如果是小项目,可以使用;对于大型项目,不建议采用这种方式。
app.config['DEBUG'] = True
# 或者使用update(config是字典的子类)
app.config.update(...)
6.2 配置方法2
通过py文件配置
# 第一个参数是py文件路径,可以是绝对路径,也可以是相对路径
app.config.from_pyfile("settings.py") # 这里是相对路径
settings.py
DEBUG = True
6.3 配置方法3
通过对象来配置
# 第一个参数可以是
# - 字符串:在这种情况下,字符串对应的对象会被导入
# - 对象:直接传入一个对象
app.config.from_object('settings.TestingConfig')
settings.py
class Config(object):
DEBUG = False
TESTING = False
DATABASE_URI = 'sqlite://:memory:'
class ProductionConfig(Config):
# 生产环境
DATABASE_URI = 'mysql://user@localhost/foo'
class DevelopmentConfig(Config):
# 开发环境
DEBUG = True
class TestingConfig(Config):
# 测试环境
TESTING = True
这种方式的好处在于,事先定义好每个环境的配置文件,然后只需要在app.config.from_object()
配置就能切换。
6.4 其他配置方法
通过文件、json、环境变量配置,详见这里
7 路由层转换器和参数
7.1 转换器
flask有内置的转换器,和django2.x+版本的转换器效果相同
DEFAULT_CONVERTERS = {
'default': UnicodeConverter, # 如果省略,默认是string
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
使用方法:
@app.route('/post/<int:post_id>') # 访问127.0.0.1/post/1
@app.route('/post/<post_id>') # 省略转换器,默认使用string
7.2 参数
app.route
中的参数,和app.add_url_rule
中参数一样
rule # 字符串,匹配的url,支持转换器
view_func=None # 视图函数
endpoint # 视图函数别名,用于反向生成URL(使用url_for),如果不写默认为当前视图函数的名称,注意,不要与其它视图函数重名
methods # 允许哪些请求方式,默认为GET;
defaults # 使用defaults={'k1':'v1'}传值,视图函数加参数k1可以通过k1获取到v1
strict_slashes # 对URL最后的/符号是否严格要求,如果为False则不严格。
redirect_to # 重定向到指定地址,可以是字符串url地址,或者是可调用的函数(函数返回值是url地址)
subdomain # 允许哪些子域访问
flask本质调用了werkzeug的Rule的init方法,所以更多详情详见这里。
8 flask的CBV
原理和django的CBV基本相同
from flask import Flask
from flask import views # 导入基类
app=Flask(__name__)
class LoginView(views.View):
# 需要重写dispatch_request方法
methods = ['GET']
def dispatch_request(self):
return 'xxx'
# 这里和django不同的是必须要传一个视图函数的别名,也就是endpoint
app.add_url_rule('/login',view_func=LoginView.as_view('login'))
也可以直接继承MethodView
,就不用手动重写dispatch_request
方法了。
def outer(func):
def inner(*args, **kwargs):
print('before')
result = func(*args, **kwargs)
print('after')
return result
return inner
class LoginView(views.MethodView):
methods = ['GET'] # 指定允许的请求方式
decorators = [outer, ] # 可以增加多个装饰器,内部是for循环执行的,和装饰器从上到下一样
def get(self):
print('xxxxx')
return "get"
def post(self):
return 'post'
app.add_url_rule('/login',view_func=LoginView.as_view('login'))
9 pycharm设置jinja模版自动补全
第一步,右键模版文件夹--->Mark Directory as--->Template Folder
,此时可能会提示你还未选模板语言,是否要选择一个?
,点击确定,
如果没有提示也没关系,打开设置,找到下图位置,选择jinja2即可
10 request请求对象方法
request.method # 请求方法
request.args # get请求携带的数据
request.form # post请求提交的数据
request.values # post和get提交的所有数据
request.cookies # 浏览器携带的cookie
request.headers # 请求头
request.path # 不带域名,请求地址,不带参数
request.full_path # 不带域名,请求地址,带参数
request.url # 带域名带参数的请求地址
request.base_url # 带域名不带参数请求地址
request.url_root # 域名
request.host_url # 域名
request.host # 127.0.0.1:5000
request.files # 文件
11 response响应对象
return "字符串" # 直接返回字符串
return render_template('html模板路径',**{}) # 渲染模版,可以传自定义键值
return redirect('/index.html') # 重定向
return jsonify({'k1':'v1'}) # json格式响应,相当于django的Jsonresponse
# make_response用于响应头里添加自定义信息,比如session、cookies,使用这个方法包装一个response对象,比如下面的例子:
# response是flask.wrappers.Response对象
# def index():
# response = make_response(render_template('index.html', foo=42))
# response.headers['X-Parachutes'] = 'parachutes are cool'
# return response
response.delete_cookie('key')
response.set_cookie('key', 'value')
response.headers['X-Something'] = 'A value'
return response
12 session
from flask import Flask,sessions
在使用session之前需要在配置文件中设置SECRET_KEY
然后在视图函数中直接使用
session['key']=value # 设置session
session.pop('key') # 删除session
session['key'] # 获取session
13 消息闪现
闪现系统的基本工作方式是:在且只在下一个请求中访问上一个请求结束时记录的消息。
flash("xxxx") # 用于闪现一个消息
get_flashed_message() # 用于获取消息
在a页面设置flash,然后在b页面获取,并且只能获取一次。一般结合布局模板来使用闪现系统。此外还支持分类,更多内容详见这里。
14 请求扩展
相当于django的中间件。
from flask import Flask, Request
app = Flask(__name__, template_folder='templates')
app.debug = True
# 仅第一次请求来的时候会执行,后面再来的请求不会执行
@app.before_first_request
def before_first_request1():
print('before_first_request1')
@app.before_first_request
def before_first_request2():
print('before_first_request2')
# 类似于django的process_request,请求来之前做一些操作(比如登录认证)
@app.before_request
def before_request1():
Request.nnn = 123
print('before_request1')
@app.before_request
def before_request2():
print('before_request2')
# 请求走之前执行,可以在这里做一些操作(比如设置session等)
@app.after_request
def after_request1(response):
print('before_request1', response)
return response
@app.after_request
def after_request2(response):
print('before_request2', response)
return response
# 只要出现404状态码,就交由该视图处理,可以render一个页面
@app.errorhandler(404)
def page_not_found(error):
return 'This page does not exist', 404
# 全局的模版,{{sb(1,2)}}
@app.template_global()
def sb(a1, a2):
return a1 + a2
# 全局过滤器,{{ 1|db(2,3)}}
@app.template_filter()
def db(a1, a2, a3):
return a1 + a2 + a3
@app.route('/')
def hello_world():
return "helloworld"
if __name__ == '__main__':
app.run()
15 蓝图
为了在一个或多个应用中,使应用模块化并且支持常用方案,Flask 引入了蓝图概念,主要是为应用提供目录划分,类似于django的多个app注册。
多个应用注册时,可以不使用蓝图,但是这样会导致每个app都使用自己独立的配置,且只能在 WSGI 层中管理应用。而如果使用蓝图,那么应用会在Flask层中进行管理,共享配置,通过注册按需改变应用对象。蓝图的缺点是一旦应用被创建后,只有销毁整个应用对象才能注销蓝图。
要使用蓝图,首先要注册蓝图
from flask import Flask
from yourapplication.simple_page import simple_page
# 正常创建app,在app里注册蓝图
app = Flask(__name__)
app.register_blueprint(simple_page)
然后在应用下使用蓝图
from flask import Blueprint, render_template, abort
from jinja2 import TemplateNotFound
# 使用时,用Blueprint代替Flask创建应用
simple_page = Blueprint('simple_page', __name__,
template_folder='templates',static_folder='statics',static_url_path='/static')
@simple_page.route('/', defaults={'page': 'index'})
@simple_page.route('/<page>')
def show(page):
try:
return render_template(f'pages/{page}.html')
except TemplateNotFound:
abort(404)
一般来说把项目划分成几个部分,比如模版、视图、静态文件、中间件等等,使用蓝图注册视图层下的应用,统一管理。
蓝图的应用示例,参考这里。
大型项目的目录结构:点击下载
简单项目的目录结构:点击下载
16 信号
Flask 自 0.6 版本开始在内部支持信号。信号功能由优秀的 blinker 库提供支持, 如果没有安装该库就无法使用信号功能,但不影响其他功能。
什么是信号?当flask的运行过程中或者是在flask扩展中发生动作时,会设置一些信号,默认是不会执行的。当你需要使用信号的时候,订阅某个信号,为它绑定你的函数,它就会执行,并且所有的信号处理器是乱序执行的。
flask内置了如下信号
template_rendered = _signals.signal('template-rendered') # 模板渲染后执行
before_render_template = _signals.signal('before-render-template') # 模板渲染前执行
request_started = _signals.signal('request-started') # 请求到来前执行
request_finished = _signals.signal('request-finished') # 请求结束后执行
request_tearing_down = _signals.signal('request-tearing-down') # 请求执行完毕后自动执行(无论成功与否)
got_request_exception = _signals.signal('got-request-exception') # 请求执行出现异常时执行
appcontext_tearing_down = _signals.signal('appcontext-tearing-down') # 应用上下文执行完毕后自动执行(无论成功与否)
appcontext_pushed = _signals.signal('appcontext-pushed') # 应用上下文push时执行
appcontext_popped = _signals.signal('appcontext-popped') # 应用上下文pop时执行
message_flashed = _signals.signal('message-flashed') # 调用flash在其中添加数据时,自动触发
信号的使用:
首先需要安装blinker
pip install blinker
然后只需要定义函数,订阅信号即可
from flask import Flask,signals # 导入信号类
app = Flask(__name__)
app.config['DEBUG'] = True
# 创建函数
def func(*args, **kwargs):
print('request_started信号触发了', args, kwargs) # args[0]是当前的app对象
# 订阅信号request_started,connect里传入定义的函数
signals.request_started.connect(func)
@app.route("/",endpoint="root")
def hello_world():
print("视图函数执行")
return "hello!"
if __name__ == '__main__':
app.run()
支持自定义信号(需要自己触发信号,其余操作和内置信号使用相同):
from flask import Flask
from flask.signals import _signals
app = Flask(__name__)
app.config['DEBUG'] = True
# 自定义信号
sig = _signals.signal('xxxx')
def func( *args, **kwargs):
print(args)
print(kwargs)
# 自定义信号中注册函数
sig.connect(func)
@app.route("/",endpoint="root")
def hello_world():
# 在想要触发的位置send触发信号
sig.send("abc",k="v")
print("视图函数执行")
return "hello!"
if __name__ == '__main__':
app.run()
三、flask第三方插件
四、flask源码分析
本文来自博客园,作者:yyyz,转载请注明原文链接:https://www.cnblogs.com/yyyzyyyz/p/15586422.html