@提示: 微信搜索【猿码记】回复 【fastapi】即可获取源码信息~

在这一篇文章中,对fastapi框架和pydantic进行了升级,然后就是各种不兼容,以后再也不敢轻易升级….

  • pydantic:从1.10.11升级到2.5.2,这里有坑,里面有很多属性都给删除了,而且升级pydantic后,fastapi也必须升级,否则底层使用的pydantic会有问题;
  • fastapi: 从 0.94.0升级到0.105.0

1.介绍

在大型项目中,良好的配置管理是确保系统可维护性和灵活性的关键,FastAPI官方文档在设置和环境变量: https://fastapi.tiangolo.com/zh/advanced/settings/#_1介绍了两种方式;

1.1 方式一:export

首先使用关键词export设置配置值,如下:

export APP_NAME="FastAPI学习使用"

然后在代码中使用os.getenv("变量名","默认值")获取

import os

appName = os.getenv("APP_NAME", "")
print(appName)

上述方式,适合配置比较少的情况,常用的还是下面方式

1.2 方式二: .env+pydantic

在日常工作中比较常用的还是通过.env来设置配置,并把内容映射到pydantic对应的模型上,方便读取和管理,下面以这个方式为主进行学习。

2. 安装依赖

2.1 安装python-dotenv

python-dotenv 是一个Python库,用于从文本文件中加载环境变量

$ pip install python-dotenv

2.2 安装pydantic_settings

$ pip install pydantic_settings

3.编写配置

3.1 编写.env

# -------- 服务配置信息 --------
APP_ENV=local
APP_NAME=AI学习
APP_PORT=8080
APP_HOST=0.0.0.0
APP_VERSION=v1.0.0
APP_DEBUG=true

# -------- jwt配置 --------
# 是否开启jwt
JWT_ENABLE=false
# 秘钥
JWT_SECRET_KEY=abcd12345@abcdef
# 算法
JWT_ALGORITHM=HS256
# 过期时间(单位:分钟)
JWT_EXPIRED=60
# 签发人
JWT_ISS=猿码记
# 不校验JWT token的路由
JWT_NO_CHECK_URIS=/,/apidoc,/openapi.json,/api/user/login,/favicon.ico


# -------- 数据库配置 --------
DB_HOST=127.0.0.1
DB_PORT=3306
DB_USER=root
DB_PASSWORD=root
DB_DATABASE=test

# -------- redis配置 --------
REDIS_DSN=redis://0.0.0.0:6379/0

3.2 编写配置模型

在包app/config下,创建文件app_config.py,内容如下:

from pydantic import RedisDsn
from pydantic_settings import BaseSettings


class AppConfigSettings(BaseSettings):
"""应用配置"""
app_name: str = "FastAPI学习"
app_port: int = 8080
app_env: str = "dev"
app_debug: bool = False
"""jwt配置"""
jwt_enable: bool = False
jwt_secret_key: str = "12345789@98765431"
jwt_algorithm: str = "HS256"
jwt_expired: int = 30
jwt_iss: str = "猿码记"
jwt_no_check_uris: str = ""
"""数据库配置"""
db_host: str
db_port: int
db_user: str
db_password: str
db_database: str
"""redis配置"""
redis_dsn: RedisDsn = None

4.使用

4.1 创建配置实例

在文件app/config/__init__.py编写代码如下

from dotenv import load_dotenv
from .validate_template_config import validateChineseDict, keyErrorChineseDict
from .app_config import *

# 加载 .env 文件
load_dotenv()
# 实例化配置模型
appSettings = AppConfigSettings()

4.2 使用示例

main.py中使用

...
# 导入配置
from app.config import appSettings
...
if __name__ == "__main__":
# 打印配置
print(appSettings)
# 这里使用端口是.env 中配置的
uvicorn.run(server, host="0.0.0.0", port=appSettings.app_port)

4.3 使用优化

根据官方文档介绍,如果通过上面方式使用配置,每次导入都会创建一个配置实例,而且从磁盘中读取文件通常是一项耗时的(慢)操作,所以尽可能的复用这个配置实例。文档推荐使用@lru_cache 装饰器,它只有在第一次调用它时,才会创建配置实例;

根据文档,我们修改app/config/__init__.py 代码,修改后如下:

from functools import lru_cache
from dotenv import load_dotenv
from .validate_template_config import validateChineseDict, keyErrorChineseDict
from .app_config import *

@lru_cache
def getAppConfig() -> AppConfigSettings:
# 加载 .env 文件,dotenv_path 变量默认是.env
load_dotenv()
# 实例化配置模型
return AppConfigSettings()


# 获取配置
appSettings = getAppConfig()

4.4 lru_cache 技术细节

具体说明,可查看官方文档: https://fastapi.tiangolo.com/zh/advanced/settings/#lru_cache

@lru_cache 装饰器,相当于是缓存了函数结果,当相同入参请求时,返回的是缓存结果,不相同时则会重新计算结果,以下是官方给的代码示例和配套讲解图

a.代码示例

@lru_cache
def say_hi(name: str, salutation: str = "Ms."):
return f"Hello {salutation} {name}"

b.讲解图

5.多环境管理

项目通常都会运行在多个环境,比如开发时运行本地环境、联调时运行测试环境、发布到线上是生产环境;

5.1 多个文件

.env      # 默认为本地开发环境
.env.test # 测试环境
.env.prod # 生产环境

5.2 修改创建配置实例

在文件app/config/__init__.py编写代码如下

import os
from functools import lru_cache
import argparse
from dotenv import load_dotenv
from .validate_template_config import validateChineseDict, keyErrorChineseDict
from .app_config import *

@lru_cache
def getAppConfig() -> AppConfigSettings:
""" 获取项目配置 """
# 解析命令行参数
parseCliArgument()
# 读取运行环境
runEnv = os.environ.get("APP_ENV", "")
print("运行环境: ", runEnv)
# 默认加载.env
envFile = ".env"
# 运行环境不为空加载 .env 文件
if runEnv != "":
# 当是其他环境时,如测试环境: 加载 .env.test 正式环境: 加载.env.prod
envFile = f".env.{runEnv}"
# 加载配置
load_dotenv(envFile)
# 实例化配置模型
return AppConfigSettings()


def parseCliArgument():
""" 解析命令行参数 """
import sys
if "uvicorn" in sys.argv[0]:
print(sys.argv[0])
return
# 使用 argparse 定义命令行参数
parser = argparse.ArgumentParser(description="命令行参数")
parser.add_argument("--env", type=str, default="", help="运行环境")
# 解析命令行参数
args = parser.parse_args()
# 设置环境变量
# uvicorn模式启动,读取的.env*里面的APP_ENV
os.environ["APP_ENV"] = args.env


# 创建配置实例
appSettings = getAppConfig()

@注意: 这里使用argparse来接收命令行参数,根据不同的参数运行不通的环境

5.3 运行示例

a.使用python启动

# 默认环境 .env
$ python main.py

# 测试环境 .env.test
$ python main.py --env=test

# 生产环境 .env.prod
$ python main.py --env=prod

b.使用uvicorn启动

# 默认环境 .env
$ uvicorn main:server --port 8000 --env-file .env

# 测试环境 .env.test
$ uvicorn main:server --port 8001 --env-file .env.test

# 生产环境 .env.prod
$ uvicorn main:server --port 8002 --env-file .env.test

@注意: 使用uvicorn启动,命令行参数只能按照uvicorn的文档来,不能传自定义参数