FastAPI:在同一个设置类中加载多个环境



我一直在努力实现这一目标,现在似乎我找不到我的方式。我的FastAPI项目有以下main入口点:

from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
from starlette.responses import RedirectResponse
from app.core.config import get_api_settings
from app.api.api import api_router

def get_app() -> FastAPI:
api_settings = get_api_settings()
server = FastAPI(**api_settings.fastapi_kwargs)
server.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
server.include_router(api_router, prefix="/api")
@server.get("/", include_in_schema=False)
def redirect_to_docs() -> RedirectResponse:
return RedirectResponse(api_settings.docs_url)
return server

app = get_app()

到目前为止还没有太花哨的。正如你所看到的,我正在导入get_api_settings,它包含了我的整个服务配置,它看起来像这样:

from functools import lru_cache
from typing import Any, Dict
from pydantic import BaseSettings

class APISettings(BaseSettings):
"""This class enables the configuration of your FastAPI instance
through the use of environment variables.
Any of the instance attributes can be overridden upon instantiation by
either passing the desired value to the initializer, or by setting the
corresponding environment variable.
Attribute `xxx_yyy` corresponds to environment variable `API_XXX_YYY`.
So, for example, to override `api_prefix`, you would set the environment
variable `API_PREFIX`.
Note that assignments to variables are also validated, ensuring that
even if you make runtime-modifications to the config, they should have
the correct types.
"""
# fastapi.applications.FastAPI initializer kwargs
debug: bool = False
docs_url: str = "/docs"
openapi_prefix: str = ""
openapi_url: str = "/openapi.json"
redoc_url: str = "/redoc"
title: str = "Api Backend"
version: str = "0.1.0"
# Custom settings
disable_docs: bool = False
environment: str
@property
def fastapi_kwargs(self) -> Dict[str, Any]:
"""This returns a dictionary of the most commonly used keyword
arguments when initializing a FastAPI instance.
If `self.disable_docs` is True, the various docs-related arguments
are disabled, preventing spec from being published.
"""
fastapi_kwargs: Dict[str, Any] = {
"debug": self.debug,
"docs_url": self.docs_url,
"openapi_prefix": self.openapi_prefix,
"openapi_url": self.openapi_url,
"redoc_url": self.redoc_url,
"title": self.title,
"version": self.version
}
if self.disable_docs:
fastapi_kwargs.update({
"docs_url": None,
"openapi_url": None,
"redoc_url": None
})
return fastapi_kwargs
class Config:
case_sensitive = True
# env_file should be dynamic depending on the 
# `environment` env variable
env_file = ""
env_prefix = ""
validate_assignment = True

@lru_cache()
def get_api_settings() -> APISettings:
"""This function returns a cached instance of the APISettings object.
Caching is used to prevent re-reading the environment every time the API
settings are used in an endpoint.
If you want to change an environment variable and reset the cache
(e.g., during testing), this can be done using the `lru_cache` instance
method `get_api_settings.cache_clear()`.
"""
return APISettings()

我正在尝试为多个环境准备此服务:

  • dev

对于上面的每一个,我有三个不同的.env文件如下:

  • core/configs/dev.env
  • core/configs/stage.env
  • core/configs/prod.env

作为一个例子,.env文件是这样的:

environment=dev
frontend_service_url=http://localhost:3000

我不能得到我的头是如何动态地设置env_file = ""在我的APISettingsbasessettings类基于environment属性的Config类。

阅读Pydantic的文档,我想我可以使用customise_sources类方法来做这样的事情:

def load_envpath_settings(settings: BaseSettings):
environment =  # not sure how to access it here
for env in ("dev", "stage", "prod"):
if environment == env:
return f"app/configs/{environment}.env"

class APISettings(BaseSettings):
# ...
class Config:
case_sensitive = True
# env_file = "app/configs/dev.env"
env_prefix = ""
validate_assignment = True
@classmethod
def customise_sources(cls, init_settings, env_settings, file_secret_settings):
return (
init_settings,
load_envpath_settings,
env_settings,
file_secret_settings,
)

,但我找不到一种方法来访问load_envpath_settings中的environment。知道怎么解吗?或者有没有别的办法?我还尝试在我的APISettings类中创建另一个@property,这基本上与load_envpath_settings相同,但我无法在Config类中引用它。

第一;通常你会将你想要激活的文件复制到.env文件中,然后加载它。但是,如果您想让.env文件控制哪些配置是活动的:

您可以有两组配置-一组从.env加载初始配置(即哪个环境是活动的),另一组从core/configs/<environment>.env文件加载实际应用程序设置。

class AppSettings(BaseSettings):
environment:str = 'development'

这将受到.env中给出的配置的影响(这是默认文件名)。然后,您将使用该值通过使用_env_file参数来加载API配置,这在所有BaseSettings实例上都是支持的。

def get_app_settings() -> AppSettings:
return AppSettings()
def get_api_settings() -> APISettings:
app_settings = get_app_settings()
return APISettings(_env_file=f'core/configs/{app_settings.environment}.env')  # or os.path.join() and friends

最新更新