配置处理

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

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

无论您如何加载配置,都有一个配置对象可用,其中包含已加载的配置值:config 属性,属于 Flask 对象。 这是 Flask 本身放置某些配置值的地方,也是扩展放置其配置值的地方。 但这也是您可以拥有自己的配置的地方。

配置基础知识

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

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

某些配置值也会转发到 Flask 对象,因此您可以从中读取和写入它们

app.testing = True

要一次更新多个键,您可以使用 dict.update() 方法

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

调试模式

DEBUG 配置值很特殊,因为它在应用程序开始设置后更改时可能会表现不一致。 为了可靠地设置调试模式,请在 flaskflask run 命令中使用 --debug 选项。 在调试模式下,flask run 默认将使用交互式调试器和重载器。

$ flask --app hello run --debug

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

内置配置值

以下配置值由 Flask 内部使用

DEBUG

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

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

默认值: False

TESTING

启用测试模式。 异常将被传播,而不是由应用程序的错误处理程序处理。 扩展程序也可能会更改其行为以方便更轻松的测试。 您应该在自己的测试中启用此项。

默认值: False

PROPAGATE_EXCEPTIONS

异常将被重新引发,而不是由应用程序的错误处理程序处理。 如果未设置,则在启用 TESTINGDEBUG 时,这会隐式为 true。

默认值: None

TRAP_HTTP_EXCEPTIONS

如果没有 HTTPException 类型异常的处理程序,则重新引发它以由交互式调试器处理,而不是将其作为简单的错误响应返回。

默认值: False

TRAP_BAD_REQUEST_ERRORS

尝试从请求字典(如 argsform)访问不存在的键将返回 400 Bad Request 错误页面。 启用此选项以将错误视为未处理的异常,以便您可以获得交互式调试器。 这是 TRAP_HTTP_EXCEPTIONS 的更具体版本。 如果未设置,则在调试模式下启用。

默认值: None

SECRET_KEY

一个密钥,将用于安全地签署会话 cookie,并且可以供扩展程序或您的应用程序用于任何其他安全相关需求。 它应该是一个长的随机 bytesstr。 例如,将此输出复制到您的配置中

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

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

默认值: None

SECRET_KEY_FALLBACKS

一个旧密钥列表,仍然可以用于取消签名,最近的密钥在最前面。 这允许项目在不使活动会话或其他最近签名的密钥失效的情况下实施密钥轮换。

密钥应在适当的时间段后删除,因为检查每个额外的密钥都会增加一些开销。

Flask 的内置安全 cookie 会话支持此功能。 使用 SECRET_KEY 的扩展程序可能尚不支持此功能。

默认值: None

3.1 版本中新增。

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

默认值: 'session'

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

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

默认值: None

警告

如果在浏览器使用一个设置创建 cookie 后更改此设置,则可能会导致创建另一个 cookie。 浏览器可能会以未定义的顺序发送两者。 在这种情况下,您可能还需要更改 SESSION_COOKIE_NAME,否则会使旧会话失效。

更新日志

在 2.3 版本中更改: 默认情况下未设置,不会回退到 SERVER_NAME

会话 cookie 有效的路径。 如果未设置,则 cookie 在 APPLICATION_ROOT/ (如果未设置)下有效。

默认值: None

出于安全考虑,浏览器不允许 JavaScript 访问标记为“HTTP only”的 cookie。

默认值: True

如果 cookie 标记为“secure”,则浏览器仅会将 cookie 与 HTTPS 请求一起发送。 应用程序必须通过 HTTPS 提供服务,这样才有意义。

默认值: False

浏览器将根据顶级文档的域而不是仅根据设置 cookie 的文档的域发送 cookie。 这可以防止在 iframe 中设置的第三方 cookie 在不同的站点之间“泄漏”。

浏览器开始不允许使用未分区的第三方 cookie,因此如果您希望它们在这样的嵌入式情况下工作,则需要将 cookie 标记为已分区。

启用此选项会隐式启用 SESSION_COOKIE_SECURE,因为它仅在通过 HTTPS 提供服务时才有效。

默认值: False

3.1 版本中新增。

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

默认值: None

更新日志

1.0 版本中新增。

PERMANENT_SESSION_LIFETIME

如果 session.permanent 为 true,则 cookie 的过期时间将设置为未来此秒数。 可以是 datetime.timedeltaint

Flask 的默认 cookie 实现验证加密签名是否未超过此值。

默认值: timedelta(days=31)2678400 秒)

SESSION_REFRESH_EACH_REQUEST

控制当 session.permanent 为 true 时,是否在每个响应中都发送 cookie。 每次都发送 cookie(默认)可以更可靠地防止会话过期,但会使用更多带宽。 非永久会话不受影响。

默认值: True

USE_X_SENDFILE

在提供文件服务时,设置 X-Sendfile 标头,而不是使用 Flask 提供数据服务。 某些 Web 服务器(例如 Apache)会识别此标头并更有效地提供数据服务。 这仅在使用此类服务器时才有意义。

默认值: False

SEND_FILE_MAX_AGE_DEFAULT

在提供文件服务时,将缓存控制最大期限设置为此秒数。 可以是 datetime.timedeltaint。 使用应用程序或蓝图上的 get_send_file_max_age() 在每个文件的基础上覆盖此值。

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

默认值: None

TRUSTED_HOSTS

根据这些受信任的值验证 Request.host 和使用它的其他属性。 如果主机无效,则引发 SecurityError,这将导致 400 错误。 如果为 None,则所有主机都有效。 每个值都是完全匹配,或者可以以点 . 开头以匹配任何子域。

验证在路由期间针对此值完成。 before_requestafter_request 回调仍将被调用。

默认值: None

3.1 版本中新增。

SERVER_NAME

告知应用程序它绑定到的主机和端口。

如果启用了 subdomain_matching,则必须设置,以便能够从请求中提取子域。

必须为 url_for 设置,才能在请求上下文之外生成外部 URL。

默认值: None

在 3.1 版本中更改: 不限制请求仅限于此域,对于 subdomain_matchinghost_matching 都是如此。

更新日志

在 2.3 版本中更改: 不影响 SESSION_COOKIE_DOMAIN

在 1.0 版本中更改: 不隐式启用 subdomain_matching

APPLICATION_ROOT

告知应用程序它由应用程序/Web 服务器挂载在哪个路径下。 这用于在请求上下文之外生成 URL(在请求内部,调度程序负责设置 SCRIPT_NAME;有关调度配置的示例,请参阅 应用程序分发)。

如果未设置 SESSION_COOKIE_PATH,将用于会话 cookie 路径。

默认值: '/'

PREFERRED_URL_SCHEME

当不在请求上下文中时,使用此方案生成外部 URL。

默认值: 'http'

MAX_CONTENT_LENGTH

在此请求期间将读取的最大字节数。 如果超过此限制,则会引发 413 RequestEntityTooLarge 错误。 如果设置为 None,则在 Flask 应用程序级别不强制执行限制。 但是,如果它是 None 并且请求没有 Content-Length 标头,并且 WSGI 服务器未指示它终止流,则不会读取任何数据以避免无限流。

每个请求都默认为此配置。 可以在特定的 Request.max_content_length 上设置它,以将限制应用于该特定视图。 应根据应用程序或视图的特定需求适当设置此值。

默认值: None

更新日志

0.6 版本中新增。

MAX_FORM_MEMORY_SIZE

multipart/form-data 正文中,任何非文件表单字段可能的最大大小(以字节为单位)。 如果超过此限制,则会引发 413 RequestEntityTooLarge 错误。 如果设置为 None,则在 Flask 应用程序级别不强制执行限制。

每个请求都默认为此配置。 可以在特定的 Request.max_form_memory_parts 上设置它,以将限制应用于该特定视图。 应根据应用程序或视图的特定需求适当设置此值。

默认值: 500_000

3.1 版本中新增。

MAX_FORM_PARTS

multipart/form-data 正文中可能存在的最大字段数。 如果超过此限制,则会引发 413 RequestEntityTooLarge 错误。 如果设置为 None,则在 Flask 应用程序级别不强制执行限制。

每个请求都默认为此配置。 可以在特定的 Request.max_form_parts 上设置它,以将限制应用于该特定视图。 应根据应用程序或视图的特定需求适当设置此值。

默认值: 1_000

3.1 版本中新增。

TEMPLATES_AUTO_RELOAD

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

默认值: None

EXPLAIN_TEMPLATE_LOADING

记录调试信息,跟踪模板文件的加载方式。 这对于 выяснить 为什么模板未加载或加载了错误的文件很有用。

默认值: False

如果 cookie 标头大于此字节数,则发出警告。 默认为 4093。 较大的 cookie 可能会被浏览器静默忽略。 设置为 0 以禁用警告。

PROVIDE_AUTOMATIC_OPTIONS

设置为 False 以禁用自动添加 OPTIONS 响应。 这可以通过更改 provide_automatic_options 属性为每个路由覆盖。

3.10 版本中新增: 添加了 PROVIDE_AUTOMATIC_OPTIONS 以控制自动生成的 OPTIONS 响应的默认添加。

更新日志

在 2.3 版本中更改: 删除了 JSON_AS_ASCIIJSON_SORT_KEYSJSONIFY_MIMETYPEJSONIFY_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_REQUESTTEMPLATES_AUTO_RELOADLOGGER_HANDLER_POLICYEXPLAIN_TEMPLATE_LOADING

0.10 版本中新增: JSON_AS_ASCIIJSON_SORT_KEYSJSONIFY_PRETTYPRINT_REGULAR

0.9 版本中新增: PREFERRED_URL_SCHEME

0.8 版本中新增: TRAP_BAD_REQUEST_ERRORSTRAP_HTTP_EXCEPTIONSAPPLICATION_ROOTSESSION_COOKIE_DOMAINSESSION_COOKIE_PATHSESSION_COOKIE_HTTPONLYSESSION_COOKIE_SECURE

0.7 版本中新增: PROPAGATE_EXCEPTIONSPRESERVE_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)

从环境变量配置

除了使用环境变量指向配置文件之外,您可能会发现直接从环境控制配置值很有用(或必要)。 可以指示 Flask 使用 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_。 这可以通过 from_prefixed_env()prefix 参数进行配置。

值将被解析,以尝试将其转换为比字符串更具体的类型。默认情况下,使用 json.loads(),因此任何有效的 JSON 值都是可能的,包括列表和字典。这可以通过 from_prefixed_env()loads 参数进行配置。

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

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

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

在 Windows 上,环境变量键始终为大写,因此上面的示例最终将变为 MYAPI__CREDENTIALS__USERNAME

为了获得更多配置加载功能,包括合并和不区分大小写的 Windows 支持,请尝试使用专门的库,例如 Dynaconf,它包括与 Flask 的集成。

配置最佳实践

先前提到方法的缺点是它使测试变得稍微困难。 通常来说,对于这个问题没有 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 之类的工具将代码和配置分别推送到生产服务器。

实例文件夹

更新日志

在版本 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 或虚拟环境的路径。 您可以打印 sys.prefix 的值,以查看前缀的设置。

由于 config 对象提供了从相对文件名加载配置文件,因此我们使通过文件名进行的加载可以相对于实例路径(如果需要)。 配置文件中相对路径的行为可以通过应用程序构造函数的 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()