配置文件
Flask 中所有的配置文件可以通过Flask(_name_).config查看。实际上是一个flask.config.Config对象
from flask import Flask
app = Flask(__name__)
print(app.config)
默认配置文件
{
'DEBUG': get_debug_flag(default=False)
'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,
}
修改配置文件的方式
基于类
class DevConfig:
DEBUG = True
# ......
app.config.from_object(DevConfig)
其他修改方式
# 直接修改
app.config['DEBUG'] = True
# py文件
app.config.from_pyfile("settings.py")
# settings.py文件
DEBUG = True
# 环境变量的值为python文件名称名称,内部调用from_pyfile方法
pp.config.from_envvar("环境变量名称")
# json文件
app.config.from_json("setting.json")
# setting.json 文件
{"DEBUG": true}
# python字典
app.config.from_mapping({'DEBUG':True})
路由系统
url反向生成
endpoint 反向生成url,endpoint默认就是函数名
from flask import url_for
@app.route('/index', methods=['GET', "POST"], endpoint='xxx')
def index():
print(url_for('xxx'))
....
url动态传参
@app.route('/index/<int:nid>') # 整数
def index(nid):
print(nid,type(nid))
return 'index'
其他常见写法
@app.route('/index/<int:nid>') # 整数
@app.route('/index/<float:nid>') # 小数
@app.route('/index/<path:nid>') # 路径
@app.route('/index/<uuid:nid>') # uuid
@app.route('/index/<nid>') # 字符串/@app.route('/index/<str:nid>')
@app.route('/index') # 啥都不传
如果想反向生成url就需要额外传参
print(url_for('index', nid=123)) # 传入的数据必须和url匹配的类型一样
所有路由的对应关系
DEFAULT_CONVERTERS = {
'default': UnicodeConverter,
'string': UnicodeConverter,
'any': AnyConverter,
'path': PathConverter,
'int': IntegerConverter,
'float': FloatConverter,
'uuid': UUIDConverter,
}
不采用装饰器的写法
def index():
return 'xxx'
app.add_url_rule(rule='/index', view_func=index)
参数说明
rule url路径
view_func 视图函数
defaults=None url传参{'k':'v'}
endpoint=None 别名,为None时则等于函数名
methods 允许的请求方式
strict_slashes=True 对url最后的 / 符号是否严格要求
redirect_to=None 重定向地址
subdomain=None 子域名访问
自定义路由匹配
from werkzeug.routing import BaseConverter
# 自定义一个类,继承BaseConverter
class MyRouter(BaseConverter):
regex = 'd+'
def to_python(self, value):
return int(value)
def to_url(self, value):
return str(value)
# 添加到转换器中
app.url_map.converters['m_r'] = MyRouter
# 使用自定义路由匹配
@app.route('/index/<m_r:nid>')
def index(nid):
print(nid)
return 'xxx'
万能版
from werkzeug.routing import BaseConverter
class MyRouter(BaseConverter):
def __init__(self, map, regex):
super().__init__(map)
self.regex = regex
def to_python(self, value):
return int(value)
def to_url(self, value):
return str(value)
app.url_map.converters['MyRouter'] = MyRouter
@app.route('/index/<MyRouter("d{3}"):nid>')
def index(nid):
print(nid)
return 'xxx'
视图
FBV
from flask import Flask
app = Flask(__name__)
@app.route('/index')
def index():
return 'index'
if __name__ == '__main__':
app.run()
加装饰器
from functools import wraps
def decorate(f):
@wraps(f)
def wrapper(*args, **kwargs):
print('前')
rest = f(*args, **kwargs)
print('后')
return rest
return wrapper
@app.route('/index')
@decorate
def index():
return 'index'
CBV
from flask import Flask
from flask import views
from functools import wraps
app = Flask(__name__)
def decorate(f):
@wraps(f)
def wrapper(*args, **kwargs):
print('前')
rest = f(*args, **kwargs)
print('后')
return rest
return wrapper
class IndexView(views.MethodView):
methods = ['GET', 'POST']
decorators = [decorate, ]
def get(self, *args, **kwargs):
return 'GET'
def post(self, *args, **kwargs):
return 'POST'
app.add_url_rule('/index', view_func=IndexView.as_view('index'))
if __name__ == '__main__':
app.run()
请求&响应
from flask import request
from flask import make_response
from flask import jsonify
from flask import redirect
from flask import render_template
from werkzeug import secure_filename
@app.route('/index/<int:nid>', methods=['GET', "POST"])
def index(nid):
# 请求相关信息
# request.method
# request.args
# request.form
# request.values
# request.data
# request.json
# request.cookies
# request.headers
# request.path
# request.full_path
# request.script_root
# request.url
# request.base_url
# request.url_root
# request.host_url
# request.host
# request.files
# obj = request.files['the_file_name']
# obj.save('/var/www/uploads/' + secure_filename(f.filename))
# 响应相关信息
# return "字符串"
# return render_template('html模板路径',**{})
# return redirect('/index.html')
# return jsonify({'k':'v'})
# 定制cookie,响应头
# response 是 flask.wrappers.Response 类型
# response = make_response(render_template('index.html'))
# response.delete_cookie('key')
# response.set_cookie('key', 'value')
# response.headers['X-Something'] = 'A value'
# return response
模板渲染(jinja2)
jinja2
模板语法和 DjangoTemplates
相似,但是和Django模板相比更贴近Python的语法
定义宏
jinja2 中的宏类似python中的函数
# 定义一个宏
{% macro input(name, type='text', value='') -%}
<input type="{{ type }}" name="{{ name }}" value="{{ value }}">
{%- endmacro %}
# 调用宏
{{ input('username') }}
{{ input('password', type='password') }}
自定义标签和过滤器(特殊装饰器)
@app.template_global()
def func(v1, v2):
return v1 + v2
@app.template_filter()
def func2(v1, v2, v3):
return v1 + v2 + v3
@app.route('/index')
def index():
return render_template('index.html', a=1)
{{ func(1, 2) }}
{{ a | func2(2, 3) }}
session
在 Flask 中操作 session 的方式和操作 Python 字典的方式几乎一样,跟Django的session一样,Flask也有SECRET_KEY
,但是Flask中默认SECRET_KEY=None
需要手动指定
app.secret_key = 'xxx'
或者直接在配置文件中指定
class DevConfig:
DEBUG = True
SECRET_KEY = 'xxx'
# ......
app.config.from_object(DevConfig)
- 当请到来时,Flask会读取cookie中session这个key对应的value,并且将value解密、反序列化为Python的字典,然后放到内存中,供视图函数使用
- 当请求结束时,Flask会读取内存中session字典的值,然后再进行序列化、加密,最后写入到cookie中
session常用操作
from flask import session
session.update({"is_login":True, 'user':'xxx'})
session.get('is_login')
del session['xxx']
session.items()
session.clear()
闪现(flash)
flash一个基于session实现的用于保存数据的集合,读取时使用pop将其移出,所以闪现的特点是:使用一次就删除。
from flask import flash, get_flashed_messages
@app.route('/page1')
def page1():
flash(1,'info')
flash(11,'error')
flash([1,2,3],'xxx')
return '1'
@app.route('/page2')
def page2():
print(get_flashed_messages(category_filter=['info']))
return '2'
闪现机制内部源码
def flash(message, category="message"):
flashes = session.get("_flashes", [])
flashes.append((category, message))
session["_flashes"] = flashes
message_flashed.send(
current_app._get_current_object(), message=message, category=category
)
def get_flashed_messages(with_categories=False, category_filter=()):
flashes = _request_ctx_stack.top.flashes
if flashes is None:
_request_ctx_stack.top.flashes = flashes = (
session.pop("_flashes") if "_flashes" in session else []
)
if category_filter:
flashes = list(filter(lambda f: f[0] in category_filter, flashes))
if not with_categories:
return [x[1] for x in flashes]
return flashes
中间件
一个简单的flask示例
from flask import Flask
app = Flask(__name__)
@app.route('/index')
def index():
return 'index'
if __name__ == '__main__':
app.run()
当程序执行时,就会执行 app.run()
方法,在run
方法内部又执行了run_simple
,所以run_simple
就是整个程序的入口,源码如下
def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
if os.environ.get("FLASK_RUN_FROM_CLI") == "true":
from .debughelpers import explain_ignored_app_run
explain_ignored_app_run()
return
if get_load_dotenv(load_dotenv):
cli.load_dotenv()
# if set, let env vars override previous values
if "FLASK_ENV" in os.environ:
self.env = get_env()
self.debug = get_debug_flag()
elif "FLASK_DEBUG" in os.environ:
self.debug = get_debug_flag()
# debug passed to method overrides all other sources
if debug is not None:
self.debug = bool(debug)
_host = "127.0.0.1"
_port = 5000
server_name = self.config.get("SERVER_NAME")
sn_host, sn_port = None, None
if server_name:
sn_host, _, sn_port = server_name.partition(":")
host = host or sn_host or _host
# pick the first value that's not None (0 is allowed)
port = int(next((p for p in (port, sn_port) if p is not None), _port))
options.setdefault("use_reloader", self.debug)
options.setdefault("use_debugger", self.debug)
options.setdefault("threaded", True)
cli.show_server_banner(self.env, self.debug, self.name, False)
from werkzeug.serving import run_simple
try:
run_simple(host, port, self, **options)
finally:
# reset the first request information if the development server
# reset normally. This makes it possible to restart the server
# without reloader and that stuff from an interactive shell.
self._got_first_request = False
当请求进来时,就会执行app()
,此时就会调用app.__call__
,__call__
源码如下
def __call__(self, environ, start_response):
"""The WSGI server calls the Flask application object as the
WSGI application. This calls :meth:`wsgi_app` which can be
wrapped to applying middleware."""
return self.wsgi_app(environ, start_response)
通过源码分析,我们可以人为的添加一个中间件,但是基本上我们不用这种方式的中间件。但是在开发中一般用before_request
和after_request
装饰器,在执行业务代码之前或之后进行一些操作
from flask import Flask
app = Flask(__name__)
@app.route('/index')
def index():
return 'index'
class Middleware:
def __init__(self, wsgi_app):
self.wsgi_app = wsgi_app
def __call__(self, *args, **kwargs):
print('请求来了')
rest = self.wsgi_app(*args, **kwargs)
print('请求走了')
return rest
if __name__ == '__main__':
app.wsgi_app = Middleware(app.wsgi_app)
app.run()
蓝图(BluePrint)
作用:给开发者提供目录结构
- 子域名控制(任选其一)
- user = Blueprint('user', _name_, subdomain='user')
- app.register_blueprint(user, subdomain='user')
- url前缀(任选其一)
- app.register_blueprint(ac, url_prefix='/account')
- ac= Blueprint('ac', _name_, url_prefix='/account')
特殊装饰器
before_request,after_request
类似Django process_request
process_response
。用户登陆验证例子
# coding=utf-8
from flask import Flask
from flask import request
from flask import session
from flask import render_template
from flask import redirect
from flask import url_for
app = Flask(__name__)
app.secret_key = 'dsadsadasdsadsa'
@app.before_request
def before_request():
if request.path != url_for('login'):
if not session.get('is_login'):
return redirect('login')
@app.after_request
def after_request(response):
print(f'用户访问了:{request.path}')
return response
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html')
user = request.form.get('username')
pwd = request.form.get('password')
if user == 'test' and pwd == '123':
session.update({"is_login": True, 'user': user})
return redirect('index')
return redirect('login')
@app.route('/index')
def index():
user = session.get('user')
return f"首页! 欢迎 {user}"
if __name__ == '__main__':
app.run()
如果有多个before_request,after_request,before_request按定义的顺序执行,after_request倒叙执行
before_first_request
只有第一个请求过来时才执行,剩下的请求不执行
@app.before_first_request
def first():
print('xxx')
errorhandler
@app.errorhandler(404)
def page404(msg):
print(msg)
return '404'