Movatterモバイル変換


[0]ホーム

URL:


跳转至
Join theFastAPI Cloud waiting list 🚀
Follow@fastapi onX (Twitter) to stay updated
FollowFastAPI onLinkedIn to stay updated
Subscribe to theFastAPI and friends newsletter 🎉
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor
sponsor

设置和环境变量

🌐 由 AI 与人类协作翻译

本翻译由人类引导的 AI 生成。🤝

可能存在误解原意或不够自然等问题。🤖

你可以通过帮助我们更好地引导 AI LLM来改进此翻译。

英文版本

在许多情况下,你的应用可能需要一些外部设置或配置,例如密钥、数据库凭据、电子邮件服务的凭据等。

这些设置中的大多数是可变的(可能会改变),例如数据库 URL。并且很多可能是敏感的,比如密钥。

因此,通常会将它们提供为由应用程序读取的环境变量。

提示

要理解环境变量,你可以阅读环境变量

类型和验证

这些环境变量只能处理文本字符串,因为它们在 Python 之外,并且必须与其他程序及系统的其余部分兼容(甚至与不同的操作系统,如 Linux、Windows、macOS)。

这意味着,在 Python 中从环境变量读取的任何值都是str 类型,任何到不同类型的转换或任何验证都必须在代码中完成。

Pydantic 的Settings

幸运的是,Pydantic 提供了一个很好的工具来处理来自环境变量的这些设置:Pydantic: Settings management

安装pydantic-settings

首先,确保你创建并激活了虚拟环境,然后安装pydantic-settings 包:

$pipinstallpydantic-settings---> 100%

当你用以下方式安装all 扩展时,它也会被一并安装:

$pipinstall"fastapi[all]"---> 100%

创建Settings 对象

从 Pydantic 导入BaseSettings 并创建一个子类,这与创建 Pydantic 模型非常相似。

与 Pydantic 模型一样,用类型注解声明类属性,也可以指定默认值。

你可以使用与 Pydantic 模型相同的验证功能和工具,例如不同的数据类型,以及使用Field() 进行附加验证。

fromfastapiimportFastAPIfrompydantic_settingsimportBaseSettingsclassSettings(BaseSettings):app_name:str="Awesome API"admin_email:stritems_per_user:int=50settings=Settings()app=FastAPI()@app.get("/info")asyncdefinfo():return{"app_name":settings.app_name,"admin_email":settings.admin_email,"items_per_user":settings.items_per_user,}

提示

如果你想要一个可以快速复制粘贴的示例,请不要使用这个示例,使用下面最后一个示例。

当你创建该Settings 类的实例(此处是settings 对象)时,Pydantic 会以不区分大小写的方式读取环境变量,因此,大写变量APP_NAME 仍会用于属性app_name

接着它会转换并验证数据。因此,当你使用该settings 对象时,你将获得你声明的类型的数据(例如items_per_user 将是int)。

使用settings

然后你可以在应用中使用新的settings 对象:

fromfastapiimportFastAPIfrompydantic_settingsimportBaseSettingsclassSettings(BaseSettings):app_name:str="Awesome API"admin_email:stritems_per_user:int=50settings=Settings()app=FastAPI()@app.get("/info")asyncdefinfo():return{"app_name":settings.app_name,"admin_email":settings.admin_email,"items_per_user":settings.items_per_user,}

运行服务器

接下来,运行服务器,并把配置作为环境变量传入,例如你可以设置ADMIN_EMAILAPP_NAME

$ADMIN_EMAIL="deadpool@example.com"APP_NAME="ChimichangApp"fastapirunmain.py<span style="color: green;">INFO</span>:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

提示

要为单个命令设置多个环境变量,只需用空格分隔它们,并把它们都放在命令前面。

然后,admin_email 设置将为"deadpool@example.com"

app_name 将为"ChimichangApp"

items_per_user 会保持默认值50

在另一个模块中放置设置

你可以把这些设置放在另一个模块文件中,就像你在更大的应用 - 多个文件中看到的那样。

例如,可以有一个config.py 文件:

frompydantic_settingsimportBaseSettingsclassSettings(BaseSettings):app_name:str="Awesome API"admin_email:stritems_per_user:int=50settings=Settings()

然后在main.py 文件中使用它:

fromfastapiimportFastAPIfrom.configimportsettingsapp=FastAPI()@app.get("/info")asyncdefinfo():return{"app_name":settings.app_name,"admin_email":settings.admin_email,"items_per_user":settings.items_per_user,}

提示

你还需要一个__init__.py 文件,就像你在更大的应用 - 多个文件中看到的那样。

在依赖项中提供设置

在某些情况下,从依赖项中提供设置可能更有用,而不是在所有地方都使用一个全局的settings 对象。

这在测试期间尤其有用,因为可以很容易地用你自己的自定义设置覆盖依赖项。

配置文件

延续上一个示例,你的config.py 文件可能如下所示:

frompydantic_settingsimportBaseSettingsclassSettings(BaseSettings):app_name:str="Awesome API"admin_email:stritems_per_user:int=50
🤓 Other versions and variants

Tip

Prefer to use theAnnotated version if possible.

frompydantic_settingsimportBaseSettingsclassSettings(BaseSettings):app_name:str="Awesome API"admin_email:stritems_per_user:int=50

注意,现在我们不再创建默认实例settings = Settings()

主应用文件

现在我们创建一个依赖项,返回一个新的config.Settings()

fromfunctoolsimportlru_cachefromtypingimportAnnotatedfromfastapiimportDepends,FastAPIfrom.configimportSettingsapp=FastAPI()@lru_cachedefget_settings():returnSettings()@app.get("/info")asyncdefinfo(settings:Annotated[Settings,Depends(get_settings)]):return{"app_name":settings.app_name,"admin_email":settings.admin_email,"items_per_user":settings.items_per_user,}
🤓 Other versions and variants

Tip

Prefer to use theAnnotated version if possible.

fromfunctoolsimportlru_cachefromfastapiimportDepends,FastAPIfrom.configimportSettingsapp=FastAPI()@lru_cachedefget_settings():returnSettings()@app.get("/info")asyncdefinfo(settings:Settings=Depends(get_settings)):return{"app_name":settings.app_name,"admin_email":settings.admin_email,"items_per_user":settings.items_per_user,}

提示

我们稍后会讨论@lru_cache

目前你可以把get_settings() 当作普通函数。

然后我们可以在路径操作函数中将其作为依赖项引入,并在需要的任何地方使用它。

fromfunctoolsimportlru_cachefromtypingimportAnnotatedfromfastapiimportDepends,FastAPIfrom.configimportSettingsapp=FastAPI()@lru_cachedefget_settings():returnSettings()@app.get("/info")asyncdefinfo(settings:Annotated[Settings,Depends(get_settings)]):return{"app_name":settings.app_name,"admin_email":settings.admin_email,"items_per_user":settings.items_per_user,}
🤓 Other versions and variants

Tip

Prefer to use theAnnotated version if possible.

fromfunctoolsimportlru_cachefromfastapiimportDepends,FastAPIfrom.configimportSettingsapp=FastAPI()@lru_cachedefget_settings():returnSettings()@app.get("/info")asyncdefinfo(settings:Settings=Depends(get_settings)):return{"app_name":settings.app_name,"admin_email":settings.admin_email,"items_per_user":settings.items_per_user,}

设置与测试

接着,在测试期间,通过为get_settings 创建依赖项覆盖,就可以很容易地提供一个不同的设置对象:

fromfastapi.testclientimportTestClientfrom.configimportSettingsfrom.mainimportapp,get_settingsclient=TestClient(app)defget_settings_override():returnSettings(admin_email="testing_admin@example.com")app.dependency_overrides[get_settings]=get_settings_overridedeftest_app():response=client.get("/info")data=response.json()assertdata=={"app_name":"Awesome API","admin_email":"testing_admin@example.com","items_per_user":50,}
🤓 Other versions and variants

Tip

Prefer to use theAnnotated version if possible.

fromfastapi.testclientimportTestClientfrom.configimportSettingsfrom.mainimportapp,get_settingsclient=TestClient(app)defget_settings_override():returnSettings(admin_email="testing_admin@example.com")app.dependency_overrides[get_settings]=get_settings_overridedeftest_app():response=client.get("/info")data=response.json()assertdata=={"app_name":"Awesome API","admin_email":"testing_admin@example.com","items_per_user":50,}

在依赖项覆盖中,我们在创建新的Settings 对象时为admin_email 设置了一个新值,然后返回该新对象。

然后我们可以测试它是否被使用。

读取.env 文件

如果你有许多设置可能经常变化,或在不同环境中不同,那么把它们放进一个文件中,然后像环境变量一样从中读取,可能非常有用。

这种做法非常常见:这些环境变量通常放在名为.env 的文件中,该文件被称为 “dotenv”。

提示

以点(.)开头的文件在类 Unix 系统(如 Linux 和 macOS)中是隐藏文件。

但 dotenv 文件并不一定必须是这个确切的文件名。

Pydantic 支持使用一个外部库来从这类文件中读取。你可以在Pydantic Settings: Dotenv (.env) support 中阅读更多信息。

提示

要使其工作,你需要执行pip install python-dotenv

.env 文件

你可以有一个.env 文件,内容如下:

ADMIN_EMAIL="deadpool@example.com"APP_NAME="ChimichangApp"

.env 中读取设置

然后更新config.py

frompydantic_settingsimportBaseSettings,SettingsConfigDictclassSettings(BaseSettings):app_name:str="Awesome API"admin_email:stritems_per_user:int=50model_config=SettingsConfigDict(env_file=".env")
🤓 Other versions and variants

Tip

Prefer to use theAnnotated version if possible.

frompydantic_settingsimportBaseSettings,SettingsConfigDictclassSettings(BaseSettings):app_name:str="Awesome API"admin_email:stritems_per_user:int=50model_config=SettingsConfigDict(env_file=".env")

提示

model_config 属性仅用于 Pydantic 配置。你可以在Pydantic: Concepts: Configuration 中阅读更多信息。

这里我们在你的 PydanticSettings 类中定义配置项env_file,并将其设置为我们想要使用的 dotenv 文件名。

使用lru_cache 仅创建一次Settings

从磁盘读取文件通常是一个代价较高(缓慢)的操作,所以你可能希望只在第一次读取,然后复用同一个设置对象,而不是为每个请求都重新读取。

但是,每次我们执行:

Settings()

都会创建一个新的Settings 对象,并且在创建时会再次读取.env 文件。

如果依赖项函数是这样的:

defget_settings():returnSettings()

我们就会为每个请求创建该对象,并为每个请求读取.env 文件。 ⚠️

但由于我们在顶部使用了@lru_cache 装饰器,Settings 对象只会在第一次调用时创建一次。 ✔️

fromfunctoolsimportlru_cachefromtypingimportAnnotatedfromfastapiimportDepends,FastAPIfrom.importconfigapp=FastAPI()@lru_cachedefget_settings():returnconfig.Settings()@app.get("/info")asyncdefinfo(settings:Annotated[config.Settings,Depends(get_settings)]):return{"app_name":settings.app_name,"admin_email":settings.admin_email,"items_per_user":settings.items_per_user,}
🤓 Other versions and variants

Tip

Prefer to use theAnnotated version if possible.

fromfunctoolsimportlru_cachefromfastapiimportDepends,FastAPIfrom.importconfigapp=FastAPI()@lru_cachedefget_settings():returnconfig.Settings()@app.get("/info")asyncdefinfo(settings:config.Settings=Depends(get_settings)):return{"app_name":settings.app_name,"admin_email":settings.admin_email,"items_per_user":settings.items_per_user,}

接着,对于后续请求中依赖项里对get_settings() 的任何调用,它不会再次执行get_settings() 的内部代码并创建新的Settings 对象,而是会一遍又一遍地返回第一次调用时返回的那个相同对象。

lru_cache 技术细节

@lru_cache 会修改它所装饰的函数,使其返回第一次返回的相同值,而不是每次都重新计算并执行函数代码。

因此,下面的函数会针对每个参数组合执行一次。然后,当以完全相同的参数组合调用该函数时,将重复使用该参数组合先前返回的值。

例如,如果你有一个函数:

@lru_cachedefsay_hi(name:str,salutation:str="Ms."):returnf"Hello{salutation}{name}"

你的程序可能会像这样执行:

sequenceDiagramparticipant code as Codeparticipant function as say_hi()participant execute as Execute function    rect rgba(0, 255, 0, .1)        code ->> function: say_hi(name="Camila")        function ->> execute: 执行函数代码        execute ->> code: 返回结果    end    rect rgba(0, 255, 255, .1)        code ->> function: say_hi(name="Camila")        function ->> code: 返回存储的结果    end    rect rgba(0, 255, 0, .1)        code ->> function: say_hi(name="Rick")        function ->> execute: 执行函数代码        execute ->> code: 返回结果    end    rect rgba(0, 255, 0, .1)        code ->> function: say_hi(name="Rick", salutation="Mr.")        function ->> execute: 执行函数代码        execute ->> code: 返回结果    end    rect rgba(0, 255, 255, .1)        code ->> function: say_hi(name="Rick")        function ->> code: 返回存储的结果    end    rect rgba(0, 255, 255, .1)        code ->> function: say_hi(name="Camila")        function ->> code: 返回存储的结果    end

在我们的依赖项get_settings() 的情况下,该函数甚至不接受任何参数,因此它始终返回相同的值。

这样,它的行为几乎就像是一个全局变量。但由于它使用了依赖项函数,我们可以在测试时很容易地覆盖它。

@lru_cachefunctools 的一部分,它属于 Python 标准库。你可以在Python 文档中关于@lru_cache 的章节阅读更多信息。

小结

你可以使用 Pydantic Settings 来处理应用的设置或配置,享受 Pydantic 模型的全部能力。

  • 通过使用依赖项,你可以简化测试。
  • 你可以与它一起使用.env 文件。
  • 使用@lru_cache 可以避免为每个请求反复读取 dotenv 文件,同时允许你在测试时进行覆盖。

[8]ページ先頭

©2009-2026 Movatter.jp