配置管理
应用总是需要一定的配置的。根据应用环境不同,会需要不同的配置。比如开关调试模式、设置密钥以及其他依赖于环境的东西。
Flask 的设计思路是在应用开始时载入配置。你可以在代码中直接硬编码写入配置,对于许多小应用来说这不一定是一件坏事,但是还有更好的方法。
不管你使用何种方式载入配置,都可以使用 Flask
对象的config
属性来操作配置的值。 Flask 本身就使用这个对象来保存一些配置,扩展也可以使用这个对象保存配置。同时这也是你保存配置的地方。
配置入门
config
实质上是一个字典的子类,可以像字典一样操作:
app = Flask(__name__)
app.config['TESTING'] = True
某些配置值还转移到了 Flask
对象中,可以直接通过Flask
来操作:
app.testing = True
一次更新多个配置值可以使用 dict.update()
方法:
app.config.update(
TESTING=True,
SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/'
)
环境和调试特征
ENV
和 DEBUG
配置值是特殊的,因为它们如果在应用设置完成之后改变,那么可以会有不同的行为表现。为了重可靠的设置环境和调试, Flask 使用环境变量。
环境用于为 Flask 、扩展和其他程序(如 Sentry )指明 Flask 运行的情境是什么。环境由 FLASK_ENV
环境变量控制,缺省值为 production
。
把 FLASK_ENV
设置为 development
可以打开调试模式。在调试模式下, flask run
会缺省使用交互调试器和重载器。如果需要脱离环境,单独控制调试模式,请使用FLASK_DEBUG
标示。
Changelog
Changed in version 1.0: Added FLASK_ENV
to control the environment separatelyfrom debug mode. The development environment enables debug mode.
为把 Flask 转换到开发环境并开启调试模式,设置 FLASK_ENV
:
$ export FLASK_ENV=development
$ flask run
(在 Windows 下,使用 set
代替 export
。)
推荐使用如上文的方式设置环境变量。虽然可以在配置或者代码中设置环境变量无法及时地被 flask
命令读取,一个系统或者扩展就可能会使用自己已定义的环境变量。
内置配置变量
以下配置变量由 Flask 内部使用:
ENV
- 应用运行于什么环境。 Flask 和 扩展可以根据环境不同而行为不同,如打开或关闭调试模式。
env
属性映射了这个配置键。本变量由FLASK_ENV
环境变量设置。如果本变量是在代码中设置的话,可能出现意外。
在生产环境中不要使用 development 。
缺省值: 'production'
Changelog
New in version 1.0.
DEBUG
- 是否开启调试模式。使用
flask run
启动开发服务器时,遇到未能处理的异常时会显示一个交互调试器,并且当代码变动后服务器会重启。debug
属性映射了这个配置键。当ENV
是'development'
时,本变量会启用,并且会被FLASK_DEBUG
环境变量重载。如果本变量是在代码中设置的话,可能会出现意外。
在生产环境中不要开启调试模式。
缺省值:当 ENV
是 'development'
时,为 True
;否则为False
。
缺省值: False
缺省值: None
PRESERVE_CONTEXT_ON_EXCEPTION
- 当异常发生时,不要弹出请求情境。在没有设置该变量的情况下,如果
DEBUG
为真,则本变量为真。这样允许调试器错误请求数据。本变量通常不需要直接设置。
缺省值: None
缺省值: False
TRAP_BAD_REQUEST_ERRORS
- 尝试操作一个请求字典中不存在的键,如
args
和form
,会返回一个400 Bad Request error 页面。开启本变量,可以把这种错误作为一个未处理的异常处理,这样就可以使用交互调试器了。本变量是一个特殊版本的TRAP_HTTP_EXCEPTIONS
。如果没有设置,本变量会在调试模式下开启。
缺省值: None
SECRET_KEY
- 密钥用于会话 cookie 的安全签名,并可用于应用或者扩展的其他安全需求。本变量应当是一个字节型长随机字符串,虽然 unicode 也是可以接受的。例如,复制如下输出到你的配置中:
$ python -c 'import os; print(os.urandom(16))'
b'_5#y2L"F4Q8z\n\xec]/'
当发贴提问或者提交代码时,不要泄露密钥。
缺省值: None
缺省值: 'session'
SESSION_COOKIE_DOMAIN
- 认可会话 cookie 的域的匹配规则。如果本变量没有设置,那么 cookie 会被
SERVER_NAME
的所有子域认可。如果本变量设置为False
,那么 cookie 域不会被设置。
缺省值: None
SESSION_COOKIE_PATH
- 认可会话 cookie 的路径。如果没有设置本变量,那么路径为
APPLICATION_ROOT
,如果APPLICATION_ROOT
也没有设置,那么会是/
。
缺省值: None
缺省值: True
缺省值: False
SESSION_COOKIE_SAMESITE
- 限制来自外部站点的请求如何发送 cookie 。可以被设置为
'Lax'
(推荐)或者'Strict'
。参见 Set-Cookie 选项.
缺省值: None
Changelog
New in version 1.0.
PERMANENT_SESSION_LIFETIME
- 如果
session.permanent
为真, cookie 的有效期为本变量设置的数字,单位为秒。本变量可能是一个datetime.timedelta
或者一个int
。
Flask 的缺省 cookie 机制会验证电子签章不老于这个变量的值。
缺省值: timedelta(days=31)
( 2678400
秒)
SESSION_REFRESH_EACH_REQUEST
- 当
session.permanent
为真时,控制是否每个响应都发送 cookie 。每次都发送 cookie (缺省情况)可以有效地防止会话过期,但是会使用更多的带宽。会持续会话不受影响。
缺省值: True
USE_X_SENDFILE
- 当使用 Flask 提供文件服务时,设置
X-Sendfile
头部。有些网络服务器,如 Apache ,识别这种头部,以利于更有效地提供数据服务。本变量只有使用这种服务器时才有效。
缺省值: False
SEND_FILE_MAX_AGE_DEFAULT
- 当提供文件服务时,设置缓存,控制最长存活期,以秒为单位。可以是一个
datetime.timedelta
或者一个int
。在一个应用或者蓝图上使用get_send_file_max_age()
可以基于单个文件重载本变量。
缺省值: timedelta(hours=12)
( 43200
秒)
如果配置了本变量, SESSION_COOKIE_DOMAIN
没有配置,那么本变量会被用于会话 cookie 的域。现代网络浏览器不会允许为没有点的域设置cookie 。为了使用一个本地域,可以在你的 host
文件中为应用路由添加任意名称。:
127.0.0.1 localhost.dev
如果这样配置了, url_for
可以为应用生成一个单独的外部 URL ,而不是一个请求情境。
缺省值: None
APPLICATION_ROOT
- 通知应用应用的根路径是什么。这个变量用于生成请求环境之外的 URL (请求内的会根据
SCRIPT_NAME
生成;参见 应用调度)。
如果 SESSION_COOKIE_PATH
没有配置,那么本变量会用于会话 cookie 路径。
缺省值: '/'
缺省值: 'http'
缺省值: None
JSON_AS_ASCII
- 把对象序列化为 ASCII-encoded JSON 。如果禁用,那么 JSON 会被返回为一个Unicode 字符串或者被
jsonify
编码为UTF-8
格式。本变量应当保持启用,因为在模块内把 JSON 渲染到 JavaScript 时会安全一点。
缺省值: True
JSON_SORT_KEYS
- 按字母排序 JSON 对象的键。这对于缓存是有用的,因为不管 Python 的哈希种子是什么都能够保证数据以相同的方式序列化。为了以缓存为代价的性能提高可以禁用它,虽然不推荐这样做。
缺省值: True
缺省值: False
缺省值: 'application/json'
缺省值: None
缺省值: False
Changelog
Changed in version 1.0: LOGGER_NAME
和 LOGGER_HANDLER_POLICY
被删除。关于配置的更多内容参见 日志 。
添加 ENV
来映射 FLASK_ENV
环境变量。
添加 SESSION_COOKIE_SAMESITE
来控制会话 cookie 的 SameSite
选项。
添加 MAX_COOKIE_SIZE
来控制来自于 Werkzeug 警告。
New in version 0.11: SESSION_REFRESH_EACH_REQUEST
, TEMPLATES_AUTO_RELOAD
,LOGGER_HANDLER_POLICY
, EXPLAIN_TEMPLATE_LOADING
New in version 0.10: JSON_AS_ASCII
, JSON_SORT_KEYS
, JSONIFY_PRETTYPRINT_REGULAR
New in version 0.9: PREFERRED_URL_SCHEME
New in version 0.8: TRAP_BAD_REQUEST_ERRORS
, TRAP_HTTP_EXCEPTIONS
,APPLICATION_ROOT
, SESSION_COOKIE_DOMAIN
,SESSION_COOKIE_PATH
, SESSION_COOKIE_HTTPONLY
,SESSION_COOKIE_SECURE
New in version 0.7: PROPAGATE_EXCEPTIONS
, PRESERVE_CONTEXT_ON_EXCEPTION
New in version 0.6: MAX_CONTENT_LENGTH
New in version 0.5: SERVER_NAME
New in version 0.4: LOGGER_NAME
使用配置文件
如果把配置放在一个单独的文件中会更有用。理想情况下配置文件应当放在应用包之外。这样可以使用不同的工具进行打包与分发( 使用 Setuptools 部署 ),而后修改配置文件也没有影响。
因此,常见用法如下:
app = Flask(__name__)
app.config.from_object('yourapplication.default_settings')
app.config.from_envvar('YOURAPPLICATION_SETTINGS')
首先从 yourapplication.default_settings 模块载入配置,然后根据YOURAPPLICATION_SETTINGS
环境变量所指向的文件的内容重载配置的值。在启动服务器前,在 Linux 或 OS X 操作系统中,这个环境变量可以在终端中使用export 命令来设置:
$ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
$ python run-app.py
* Running on http://127.0.0.1:5000/
* Restarting with reloader...
在 Windows 系统中使用内置的 set 来代替:
> set YOURAPPLICATION_SETTINGS=\path\to\settings.cfg
配置文件本身实质是 Python 文件。只有全部是大写字母的变量才会被配置对象所使用。因此请确保使用大写字母。
一个配置文件的例子:
# Example configuration
DEBUG = False
SECRET_KEY = b'_5#y2L"F4Q8z\n\xec]/'
请确保尽早载入配置,以便于扩展在启动时可以访问相关配置。除了从文件载入配置外,配置对象还有其他方法可以载入配置,详见 Config
对象的文档。
使用环境变量来配置
除了使用环境变量指向配置文件之外,你可能会发现直接从环境中控制配置值很有用(或必要)。
启动服务器之前,可以在 Linux 或 OS X 上使用 shell 中的export命令设置环境变量:
$ export SECRET_KEY='5f352379324c22463451387a0aec5d2f'
$ export MAIL_ENABLED=false
$ python run-app.py
* Running on http://127.0.0.1:5000/
在 Windows 系统中使用内置的 set
来代替:
> set SECRET_KEY='5f352379324c22463451387a0aec5d2f'
尽管这种方法很简单易用,但重要的是要记住环境变量是字符串,它们不会自动反序列化为 Python 类型。
以下是使用环境变量的配置文件示例:
import os
_mail_enabled = os.environ.get("MAIL_ENABLED", default="true")
MAIL_ENABLED = _mail_enabled.lower() in {"1", "t", "true"}
SECRET_KEY = os.environ.get("SECRET_KEY")
if not SECRET_KEY:
raise ValueError("No SECRET_KEY set for Flask application")
请注意,除了空字符串之外的任何值都将被解释为 Python 中的布尔值 True
,如果环境显式设置值为 False
,则需要注意。
确保尽早加载配置,以便扩展能够在启动时访问配置。除了从文件加载,配置对象还有其他方法可以加载。完整的参考参见 Config
类文档。
配置的最佳实践
前面提到的方法的缺点是它使测试更加困难。一般来说,这个问题没有一个 100%完美的解决方案,但你可以牢记几件事以改善这种体验:
在一个函数中创建你的应用并注册“蓝图”。这样就可以使用不同配置创建多个实例,极大方便单元测试。你可以按需载入配置。
不要编写在导入时就访问配置的代码。如果你限制自己只能通过请求访问代码,那么就可以在以后按需重设配置对象。
开发/生产
大多数应用需要一个以上的配置。最起码需要一个配置用于生产服务器,另一个配置用于开发。应对这种情况的最简单的方法总是载入一个缺省配置,并把这个缺省配置作为版本控制的一部分。然后,把需要重载的配置,如前文所述,放在一个独立的文件中:
app = Flask(__name__)
app.config.from_object('yourapplication.default_settings')
app.config.from_envvar('YOURAPPLICATION_SETTINGS')
然后你只要增加一个独立的 config.py 文件并导出YOURAPPLICATION_SETTINGS=/path/to/config.py
即可。当然还有其他方法可选,例如可以使用导入或子类。
在 Django 应用中,通常的做法是在文件的开关增加from yourapplication.defaultsettings import *
进行显式地导入,然后手工重载配置。你还可以通过检查一个 YOURAPPLICATION_MODE
之类的环境变量(变量值设置为 _production 或 development 等等)来导入不同的配置文件。
一个有趣的方案是使用类和类的继承来配置:
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
如果要使用这样的方案,那么必须使用 from_object()
:
app.config.from_object('configmodule.ProductionConfig')
注意 from_object()
不会实例化类对象。如果要操作已经实例化的类,比如读取一个属性,那么在调用 from_object()
之前应当先实例化这个类:
from configmodule import ProductionConfig
app.config.from_object(ProductionConfig())
# Alternatively, import via string:
from werkzeug.utils import import_string
cfg = import_string('configmodule.ProductionConfig')()
app.config.from_object(cfg)
在你的配置类中,实例化配置对象时允许使用 @property
class Config(object):
"""Base config, uses staging database server."""
DEBUG = False
TESTING = False
DB_SERVER = '192.168.1.56'
@property
def DATABASE_URI(self): # Note: all caps
return 'mysql://user@{}/foo'.format(self.DB_SERVER)
class ProductionConfig(Config):
"""Uses production database server."""
DB_SERVER = '192.168.19.32'
class DevelopmentConfig(Config):
DB_SERVER = 'localhost'
DEBUG = True
class TestingConfig(Config):
DB_SERVER = 'localhost'
DEBUG = True
DATABASE_URI = 'sqlite:///:memory:'
配置的方法多种多样,由你定度。以下是一些好的建议:
在版本控制中保存一个缺省配置。要么在应用中使用这些缺省配置,要么先导入缺省配置然后用你自己的配置文件来重载缺省配置。
使用一个环境变量来切换不同的配置。这样就可以在 Python 解释器外进行切换,而根本不用改动代码,使开发和部署更方便,更快捷。如果你经常在不同的项目间切换,那么你甚至可以创建代码来激活 virtualenv 并导出开发配置。
在生产应用中使用 fabric 之类的工具,向服务器分别传送代码和配置。更多细节参见 使用 Fabric 部署 方案。
实例文件夹
Changelog
New in version 0.8.
Flask 0.8 引入了实例文件夹。 Flask 花了很长时间才能够直接使用应用文件夹的路径(通过 Flask.root_path
)。这也是许多开发者载入应用文件夹外的配置的方法。不幸的是这种方法只能用于应用不是一个包的情况下,即根路径指向包的内容的情况。
Flask 0.8 引入了一个新的属性: Flask.instance_path
。它指向一个新名词:“实例文件夹”。实例文件夹应当处于版本控制中并进行特殊部署。这个文件夹特别适合存放需要在应用运行中改变的东西或者配置文件。
可以要么在创建 Flask 应用时显式地提供实例文件夹的路径,要么让 Flask 自动探测实例文件夹。显式定义使用 instance_path 参数:
app = Flask(__name__, instance_path='/path/to/instance/folder')
请记住,这里提供的路径 必须 是绝对路径。
如果 instance_path 参数没有提供,那么会使用以下缺省位置:
- 未安装的模块:
/myapp.py
/instance
- 未安装的包:
/myapp
/__init__.py
/instance
- 已安装的模块或包:
$PREFIX/lib/python2.X/site-packages/myapp
$PREFIX/var/myapp-instance
$PREFIX
是你的 Python 安装的前缀。可能是 /usr
或你的virtualenv 的路径。可以通过打印 sys.prefix
的值来查看当前的前缀的值。
既然可以通过使用配置对象来根据关联文件名从文件中载入配置,那么就可以通过改变与实例路径相关联的文件名来按需要载入不同配置。在配置文件中的关联路径的行为可以在 “关联到应用的根路径”(缺省的)和 “关联到实例文件夹”之间变换,具体通过应用构建函数中的 instance_relative_config 来实现:
app = Flask(__name__, instance_relative_config=True)
以下是一个完整的配置 Flask 的例子,从一个模块预先载入配置,然后从实例文件夹中的一个配置文件(如果这个文件存在的话)载入要重载的配置:
app = Flask(__name__, instance_relative_config=True)
app.config.from_object('yourapplication.default_settings')
app.config.from_pyfile('application.cfg', silent=True)
通过 Flask.instance_path
可以找到实例文件夹的路径。Flask 还提供一个打开实例文件夹中的文件的快捷方法: Flask.open_instance_resource()
。
举例说明:
filename = os.path.join(app.instance_path, 'application.cfg')
with open(filename) as f:
config = f.read()
# or via open_instance_resource:
with app.open_instance_resource('application.cfg') as f:
config = f.read()