配置管理

应用程序需要某种配置。根据应用程序环境的不同,您可能需要更改不同的设置,如切换调试模式、设置密钥以及其他此类特定于环境的内容。

烧瓶的设计方式通常要求配置在应用程序启动时可用。您可以在代码中硬编码配置,这对于许多小型应用程序来说实际上并不是那么糟糕,但是有更好的方法。

不管你使用何种方式载入配置,都可以使用 Flask 对象的 config 属性来操作配置的值。 Flask 本身就使用这个对象 来保存一些配置,扩展也可以使用这个对象保存配置。同时这也是你保存配置的地方。

配置入门

这个 config 实际上是字典的子类,可以像修改任何字典一样进行修改:

app = Flask(__name__)
app.config['TESTING'] = True

某些配置值也会转发到 Flask 对象,以便您可以从中读写它们:

app.testing = True

一次更新多个密钥,可以使用 dict.update() 方法:

app.config.update(
    TESTING=True,
    SECRET_KEY='192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'
)

调试模式

这个 DEBUG 配置值是特殊的,因为如果在应用程序开始设置后更改它的行为可能会不一致。为了可靠地设置调试模式,请使用 --debug 选项上的 flaskflask run 指挥部。 flask run 默认情况下,将在调试模式下使用交互式调试器和重新加载程序。

$ flask --app hello run --debug

建议使用该选项。虽然可以设置 DEBUG 在您的配置或代码中,强烈建议您这样做。它不能被早期阅读 flask run 命令,并且某些系统或扩展可能已经根据先前的值进行了自身配置。

内置配置变量

以下配置变量由 Flask 内部使用:

DEBUG

是否启用调试模式。使用时 flask run 要启动开发服务器,将为未处理的异常显示交互式调试器,并在代码更改时重新加载服务器。这个 debug 属性映射到此配置键。这是使用 FLASK_DEBUG 环境变量。如果在代码中设置,它的行为可能与预期不符。

在生产环境中部署时不启用调试模式。

默认值: False

TESTING

开启测试模式。异常会被广播而不是被应用的错误处理器处理。扩展可能也会为 了测试方便而改变它们的行为。你应当在自己的调试中开启本变量。

默认值: False

PROPAGATE_EXCEPTIONS

异常将被重新引发,而不是由应用程序的错误处理程序处理。如果未设置,则如果 TESTINGDEBUG 启用。

默认值: None

TRAP_HTTP_EXCEPTIONS

如果没有处理 HTTPException 类型异常的处理器,重新引发该异常用于被交互调试器处理,而不是作为一个简单的错误响应来返回。

默认值: False

TRAP_BAD_REQUEST_ERRORS

尝试操作一个请求字典中不存在的键,如 args 和 form ,会返回一个 400 Bad Request error 页面。开启本变量,可以把这种错误作为一个未处理的 异常处理,这样就可以使用交互调试器了。本变量是一个特殊版本的 TRAP_HTTP_EXCEPTIONS 。如果没有设置,本变量会在调试模式下开启。

默认值: None

SECRET_KEY

一个秘密密钥,将用于安全地签名会话cookie,并可用于扩展或应用程序的任何其他安全相关需求。这应该是一个很长的随机过程 bytesstr . 例如,将此输出复制到您的config::

$ python -c 'import secrets; print(secrets.token_hex())'
'192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'

在发布问题或提交代码时,不要泄露密钥。

默认值: None

会话cookie的名称。如果您已有同名的cookie,则可以更改。

默认值: 'session'

它的价值在于 Domain 会话Cookie上的参数。如果未设置,浏览器将仅将Cookie发送到设置Cookie的确切域。否则,它们也会将其发送到给定值的任何子域。

不设置此值比设置它更受限制,也更安全。

默认值: None

Changelog

在 2.3 版本发生变更: 默认情况下不设置,不会回退到 SERVER_NAME

认可会话 cookie 的路径。如果没有设置本变量,那么路径为 APPLICATION_ROOT ,如果 APPLICATION_ROOT 也没有设置,那么会是 / 。

默认值: None

为了安全起见,浏览器不允许javascript访问标记为“http only”的cookie。

默认值: True

如果cookie被标记为“安全”,浏览器将只通过https发送带有请求的cookie。必须通过HTTPS为应用程序提供服务,这样做才有意义。

默认值: False

限制来自外部站点的请求如何发送 cookie 。可以被设置为 'Lax' (推荐) 或者 'Strict' 。参见 Set-Cookie 选项.

默认值: None

Changelog

在 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.timedeltaint . 在每个文件的基础上重写此值,使用 get_send_file_max_age() 在应用程序或蓝图上。

如果 Nonesend_file 告诉浏览器使用将使用的条件请求,而不是通常更可取的定时缓存。

默认值: None

SERVER_NAME

通知应用程序它绑定到的主机和端口。需要子域路由匹配支持。

如果这样配置了, url_for 可以为应用生成一个单独的外部 URL ,而不是 一个请求情境。

默认值: None

Changelog

在 2.3 版本发生变更: 不会影响 SESSION_COOKIE_DOMAIN

APPLICATION_ROOT

通知应用程序它被应用程序/web服务器装载在哪个路径下。这用于在请求上下文之外生成url(在请求内部,dispatcher负责设置 SCRIPT_NAME 相反,请参见 应用程序调度 调度配置示例)。

如果 SESSION_COOKIE_PATH 没有配置,那么本变量会用于会话 cookie 路 径。

默认值 : '/'

PREFERRED_URL_SCHEME

当不在请求情境中时,使用此方案生成外部URL。

默认值: 'http'

MAX_CONTENT_LENGTH

在进来的请求数据中读取的最大字节数。如果本变量没有配置,并且请求没有指 定 CONTENT_LENGTH ,那么为了安全原因,不会读任何数据。

默认值: None

TEMPLATES_AUTO_RELOAD

更改模板后重新加载模板。如果未设置,将在调试模式下启用。

默认值: None

EXPLAIN_TEMPLATE_LOADING

跟踪模板文件加载方式的日志调试信息。这有助于弄清楚为什么没有加载模板或加载了错误的文件。

默认值: False

如果cookie头大于此字节数,则发出警告。默认为 4093 . 浏览器可能会悄悄地忽略较大的cookie。设置为 0 关闭警告。

Changelog

在 2.3 版本发生变更: JSON_AS_ASCIIJSON_SORT_KEYSJSONIFY_MIMETYPE ,以及 JSONIFY_PRETTYPRINT_REGULAR 都被移除了。默认设置 app.json 相反,提供程序具有相同的属性。

在 2.3 版本发生变更: ENV 被移除了。

在 2.2 版本发生变更: 已删除 PRESERVE_CONTEXT_ON_EXCEPTION

在 1.0 版本发生变更: LOGGER_NAMELOGGER_HANDLER_POLICY 被移除。见 日志 有关配置的信息。

添加 ENV 反映 FLASK_ENV 环境变量。

添加 SESSION_COOKIE_SAMESITE 来控制会话 cookie 的 SameSite 选项。

添加 MAX_COOKIE_SIZE 来控制来自于 Werkzeug 警告。

在 0.11 版本加入: SESSION_REFRESH_EACH_REQUEST, TEMPLATES_AUTO_RELOAD, LOGGER_HANDLER_POLICY, EXPLAIN_TEMPLATE_LOADING

在 0.10 版本加入: JSON_AS_ASCII, JSON_SORT_KEYS, JSONIFY_PRETTYPRINT_REGULAR

在 0.9 版本加入: PREFERRED_URL_SCHEME

在 0.8 版本加入: TRAP_BAD_REQUEST_ERRORS, TRAP_HTTP_EXCEPTIONS, APPLICATION_ROOT, SESSION_COOKIE_DOMAIN, SESSION_COOKIE_PATH, SESSION_COOKIE_HTTPONLY, SESSION_COOKIE_SECURE

在 0.7 版本加入: PROPAGATE_EXCEPTIONS, PRESERVE_CONTEXT_ON_EXCEPTION

在 0.6 版本加入: MAX_CONTENT_LENGTH

在 0.5 版本加入: SERVER_NAME

在 0.4 版本加入: LOGGER_NAME

从Python文件配置

如果可以将配置存储在单独的文件中,则配置会变得更有用,该文件最好位于实际应用程序包之外。您可以部署您的应用程序,然后为特定部署单独配置它。

常见的模式是::

app = Flask(__name__)
app.config.from_object('yourapplication.default_settings')
app.config.from_envvar('YOURAPPLICATION_SETTINGS')

这首先从 yourapplication.default_settings 然后用文件的内容重写值 YOURAPPLICATION_SETTINGS 环境变量指向。在启动服务器之前,可以在shell中设置此环境变量:

$ export YOURAPPLICATION_SETTINGS=/path/to/settings.cfg
$ flask run
 * Running on http://127.0.0.1:5000/

配置文件本身就是实际的python文件。稍后,只有大写的值实际存储在配置对象中。所以请确保对配置键使用大写字母。

以下是配置文件的示例:

# Example configuration
SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'

确保在很早的时候加载配置,以便扩展能够在启动时访问配置。配置对象上还有其他方法,可以从单个文件加载。如需完整参考,请阅读 Config 对象的文档。

从数据文件配置

也可以从文件中以您选择的格式加载配置 from_file() . 例如,从TOML文件加载:

import tomllib
app.config.from_file("config.toml", load=tomllib.load, text=False)

或从JSON文件:

import json
app.config.from_file("config.json", load=json.load)

使用环境变量配置

除了使用环境变量指向配置文件之外,您可能会发现直接从环境控制配置值是有用的(或必要的)。可以使用以下命令指示FASK将以特定前缀开头的所有环境变量加载到配置中 from_prefixed_env()

在启动服务器之前,可以在shell中设置环境变量:

$ export FLASK_SECRET_KEY="5f352379324c22463451387a0aec5d2f"
$ export FLASK_MAIL_ENABLED=false
$ flask run
 * Running on http://127.0.0.1:5000/

然后,可以通过配置加载和访问变量,其关键字等于不带前缀的环境变量名。

app.config.from_prefixed_env()
app.config["SECRET_KEY"]  # Is "5f352379324c22463451387a0aec5d2f"

前缀是 FLASK_ 默认情况下。这可以通过 prefix 的论点 from_prefixed_env()

值将被解析,以尝试将它们转换为比字符串更具体的类型。默认情况下 json.loads() 所以任何有效的JSON值都是可能的,包括列表和字典。这可以通过 loads 的论点 from_prefixed_env()

在使用默认的JSON解析添加布尔值时,只有小写的“true”和“False”是有效值。请记住,任何非空字符串都被视为 True 被 Python 攻击。

可以通过使用双下划线分隔键来在嵌套词典中设置键 (__ )。父DICT上不存在的任何中间键将被初始化为空DICT。

$ export FLASK_MYAPI__credentials__username=user123
app.config["MYAPI"]["credentials"]["username"]  # Is "user123"

在Windows上,环境变量键始终为大写,因此上述示例的结尾为 MYAPI__CREDENTIALS__USERNAME

有关更多配置加载功能,包括合并和不区分大小写的Windows支持,请尝试使用专用库,如 Dynaconf, 其中包括与烧瓶的集成。

配置最佳实践

前面提到的方法的缺点是它使测试变得更加困难。一般来说,这个问题没有一个100%的解决方案,但是您可以记住以下几点来改进这种体验:

  1. 在函数中创建应用程序并在其上注册蓝图。这样,您就可以创建多个应用程序实例,并附加不同的配置,这使得单元测试更加容易。您可以根据需要使用它传入配置。

  2. 不要在导入时编写需要配置的代码。如果您将自己限制为只请求对配置的访问,则可以稍后根据需要重新配置对象。

  3. 确保尽早加载配置,以便分机可以在呼叫时访问配置 init_app

开发/生产

大多数应用需要一个以上的配置。最起码需要一个配置用于生产服务器,另一个配置用于开发。应对这种情况的最简单的方法总是载入一个缺省配置,并把这个缺省配置作为版本控制的一部分。然后,把需要重载的配置,如前文所述,放在一个独立的文 件中:

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.default_settings import * 到文件的顶部,然后手动覆盖更改。您还可以检查环境变量,例如 YOURAPPLICATION_MODE 并设置为 productiondevelopment 等等,并基于此导入不同的硬编码文件。

一个有趣的模式也是使用类和继承进行配置:

class Config(object):
    TESTING = False

class ProductionConfig(Config):
    DATABASE_URI = 'mysql://user@localhost/foo'

class DevelopmentConfig(Config):
    DATABASE_URI = "sqlite:////tmp/foo.db"

class TestingConfig(Config):
    DATABASE_URI = 'sqlite:///:memory:'
    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."""
    TESTING = False
    DB_SERVER = '192.168.1.56'

    @property
    def DATABASE_URI(self):  # Note: all caps
        return f"mysql://user@{self.DB_SERVER}/foo"

class ProductionConfig(Config):
    """Uses production database server."""
    DB_SERVER = '192.168.19.32'

class DevelopmentConfig(Config):
    DB_SERVER = 'localhost'

class TestingConfig(Config):
    DB_SERVER = 'localhost'
    DATABASE_URI = 'sqlite:///:memory:'

配置的方法多种多样,由你定度。以下是一些好的建议:

  • 在版本控制中保存一个缺省配置。要么在应用中使用这些缺省配置,要么先导入 缺省配置然后用你自己的配置文件来重载缺省配置。

  • 使用一个环境变量来切换不同的配置。这样就可以在 Python 解释器外进行切换, 而根本不用改动代码,使开发和部署更方便,更快捷。如果你经常在不同的项目 间切换,那么你甚至可以创建代码来激活 virtualenv 并导出开发配置。

  • 使用如下工具 fabric 将代码和配置分别推送到生产服务器(S)。

实例文件夹

Changelog

在 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/pythonX.Y/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 还提供一 个打开实例文件夹中的文件的快捷方法。

举例说明:

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()