问题描述
我有一个带有现有MysqL数据库的FastAPI应用程序,并且我正在尝试使用Tortoise ORM。 (简化的)主FastAPI文件如下所示:
from fastapi import FastAPI
import os
from tortoise.contrib.fastapi import register_tortoise
# Register FastAPI main app
app = FastAPI(title="my_app")
# Database
DATABASE_URL = "MysqL://{}:{}@{}:{}/{}".format(
os.environ["MysqL_USER"],os.environ["MysqL_PASSWORD"],os.environ.get("MysqL_HOST","127.0.0.1"),os.environ.get("MysqL_PORT","3306"),os.environ.get("MysqL_DB","my_db"),)
# Register Tortoise ORM with DB
register_tortoise(
app,db_url=DATABASE_URL,modules={"models": ["models"]},generate_schemas=False,add_exception_handlers=True,)
# Test sql query
from models import Store
print(Store.get(api_key="api_key"))
...以及处于相同基本目录级别的models.py文件,如下所示:
from tortoise import fields
from tortoise.models import Model
class Store(Model):
api_key = fields.CharField(max_length=64,db_index=True)
name = fields.CharField(max_length=255)
def __str__(self):
return self.name
class Meta:
table = "stores"
File ".../site-packages/tortoise/models.py",line 265,in db
raise ConfigurationError("No DB associated to model")
tortoise.exceptions.ConfigurationError: No DB associated to model
知道为什么吗?
我想这可能是我将模型模块注册到Tortoise的方式。
我正在关注文档(https://tortoise-orm.readthedocs.io/en/latest/contrib/fastapi.html),但是“对于模型应该发现的模块”的路径/语法对我来说不是很清楚。我还尝试过向pydantic_model_creator
注册模型,尽管在文档中并不清楚为什么需要这样做(https://tortoise-orm.readthedocs.io/en/latest/examples/fastapi.html#example-fastapi)。
我宁愿不要使用register_tortoise
加载的config.json完整配置文件,根据文档看来这是可选的。
解决方法
问题来自FastAPI和Tortoise ORM的异步特性。 我们必须等待FastAPI加载和Tortoise才能注册模型。
因此,我们可以成功地执行一个异步请求,等待FastAPI加载,然后Tortoise ORM请求,如下所示:
# Test SQL query
from models import Store
@app.on_event("startup")
async def startup():
print(await Store.get(api_key="api_key"))
,
在我们的案例中,其中一个 ASGI 中间件(不支持 asgi 生命周期事件)在启动期间引发错误,导致 ASGI 生命周期事件(即 startup
)无法触发,并且没有注册模型。我们最终修补了中间件,使其仅在 scope['type']
== 'http'
时触发。
ASGI spec 声明即使在启动错误后服务器也必须继续运行。
如果在使用 lifespan.startup 消息或类型为 lifespan 的范围调用可调用应用程序时引发异常,则服务器必须继续但不发送任何生命周期事件。
然而,tortoise ORM register_tortoise
函数依赖于生命周期事件 startup
来注册模型。所以我认为 uvicorn 的寿命模式应该是 on
而不是默认的 auto
。这样,您的服务器进程将终止,而不是为配置错误的应用提供服务。
uvicorn.run("start_server:app",host="0.0.0.0",port=8080,log_level="info",lifespan='on')