配置处理

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

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

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

默认值:'session'

会话 Cookie 上 Domain 参数的值。如果未设置,浏览器只会将 Cookie 发送到设置 Cookie 的确切域名。否则,浏览器还会将其发送到给定值的所有子域名。

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

默认值:None

变更日志

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

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

默认值:None

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

默认值:True

如果 Cookie 标记为“安全”,浏览器只会通过 HTTPS 请求发送 Cookie。应用程序必须通过 HTTPS 提供,此设置才有意义。

默认值: False

限制如何通过外部网站的请求发送 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

SERVER_NAME

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

如果设置,url_for 仅使用应用程序上下文(而不是请求上下文)生成外部 URL。

默认值:None

变更日志

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

APPLICATION_ROOT

告知应用程序它由应用程序/Web 服务器挂载在哪个路径下。这用于在请求的上下文中生成 URL(在请求中,调度程序负责设置 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 以禁用警告。

变更日志

在 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 文件。只有大写值才会实际存储在后面的 config 对象中。因此,请确保为你的 config 键使用大写字母。

以下是配置文件的示例

# Example configuration
SECRET_KEY = '192b9bdd22ab9ed4d12e236c78afcb9a393ec15f71bbf5dc987d54727823bcbf'

请务必尽早加载配置,以便扩展在启动时能够访问配置。config 对象上还有其他方法可以从各个文件加载。有关完整参考,请阅读 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)

从环境变量配置

除了使用环境变量指向配置文件外,你可能觉得有必要直接从环境控制你的配置值(或这样做很有用)。可以使用 from_prefixed_env() 指示 Flask 将所有以特定前缀开头的环境变量加载到 config 中。

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

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

然后可以通过 config 加载变量并访问变量,其键等于不带前缀的环境变量名称,即

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 解释器外部完成,并且使开发和部署变得更加容易,因为你可以快速轻松地在不同的配置之间切换,而无需触及任何代码。如果你经常处理不同的项目,你甚至可以创建自己的脚本来获取源代码,激活虚拟环境并为你导出开发配置。

  • 使用 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 对象提供了从相对文件名加载配置文件的功能,因此我们让用户可以选择将通过文件名进行的加载相对于实例路径进行。config 文件中相对路径的行为可以通过应用程序构造函数中的 instance_relative_config 开关在“相对于应用程序根目录”(默认值)和“相对于实例文件夹”之间切换

app = Flask(__name__, instance_relative_config=True)

以下是如何配置 Flask 以从模块预加载 config,然后在实例文件夹中存在文件时从该文件中覆盖 config 的完整示例

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