使用asyncpg驱动时,Tortoise ORM模型无法继承default_connection?
背景信息
我正在开发一个插件化系统,按需加载Tortoise-ORM(依赖配置为tortoise-orm = {extras = ["asyncpg"], version = "^0.19.3"})模型,测试模型代码如下:
class PingModel(Model): uid = fields.TextField() ping_count = fields.IntField() def __str__(self): return self.name
使用SQLite数据库时一切正常,但切换到PostgreSQL(asyncpg驱动)后,调用模型报错。数据库配置代码:
elif 'postgres' in self.config['database']: db_config = { 'connections': { 'default': { 'engine': "tortoise.backends.asyncpg", 'credentials': { 'host': self.config['database']['postgres']['db_url'], 'port': self.config['database']['postgres']['db_port'], 'user': self.config['database']['postgres']['db_user'], 'password': self.config['database']['postgres']['db_password'], 'database': self.config['database']['postgres']['db_name'], }, }, }, 'apps': { 'models': { 'models': self.models, 'default_connection': 'default' }, }, } [...] await Tortoise.init(config=db_config) await Tortoise.generate_schemas()
报错信息:
Traceback (most recent call last): File "/home/tunacado/.cache/pypoetry/virtualenvs/fishbot-5Nu9GTXz-py3.10/lib/python3.10/site-packages/discord/commands/core.py", line 124, in wrapped ret = await coro(arg) File "/home/tunacado/.cache/pypoetry/virtualenvs/fishbot-5Nu9GTXz-py3.10/lib/python3.10/site-packages/discord/commands/core.py", line 978, in _invoke await self.callback(self.cog, ctx, **kwargs) File "/home/tunacado/Source/fishbot/fishbot/cogs/ping.py", line 17, in ping record = await PingModel.filter(uid__contains=user) File "/home/tunacado/.cache/pypoetry/virtualenvs/fishbot-5Nu9GTXz-py3.10/lib/python3.10/site-packages/tortoise/models.py", line 1255, in filter return cls._meta.manager.get_queryset().filter(*args, **kwargs) File "/home/tunacado/.cache/pypoetry/virtualenvs/fishbot-5Nu9GTXz-py3.10/lib/python3.10/site-packages/tortoise/manager.py", line 15, in get_queryset return QuerySet(self._model) File "/home/tunacado/.cache/pypoetry/virtualenvs/fishbot-5Nu9GTXz-py3.10/lib/python3.10/site-packages/tortoise/queryset.py", line 299, in __init__ super().__init__(model) File "/home/tunacado/.cache/pypoetry/virtualenvs/fishbot-5Nu9GTXz-py3.10/lib/python3.10/site-packages/tortoise/queryset.py", line 96, in __init__ self.capabilities: Capabilities = model._meta.db.capabilities File "/home/tunacado/.cache/pypoetry/virtualenvs/fishbot-5Nu9GTXz-py3.10/lib/python3.10/site-packages/tortoise/models.py", line 285, in db raise ConfigurationError( tortoise.exceptions.ConfigurationError: default_connection for the model <class 'fishbot.cogs.ping.PingModel'> cannot be None
确认传入的db_config内容正确:
{'connections': {'default': {'engine': 'tortoise.backends.asyncpg', 'credentials': {'host': 'localhost', 'port': 5432, 'user': 'myuser', 'password': 'mypassword', 'database': 'mydb'}}}, 'apps': {'models': {'models': ['fishbot.cogs.ping'], 'default_connection': 'default'}}}
尝试在模型中添加class Meta、手动修改Model._meta.default_connection均无效,直接指定模型类路径提示模块未找到。运行Tortoise官方PostgreSQL示例可正常工作,推测问题出在模型传递方式上。
问题
- 为什么模型无法继承配置中的
default_connection? - 使用Tortoise-ORM初始化PostgreSQL时,如何确保所有模型都设置好
default_connection?
解答
问题1:模型无法继承default_connection的原因
核心原因是插件化按需加载模型的时机晚于Tortoise初始化流程。Tortoise在Tortoise.init()时会遍历配置中指定的模型模块,为每个模型绑定default_connection;但如果你的模型是在初始化之后才被动态加载的,这些后加载的模型就不会被Tortoise的初始化流程处理,自然无法继承配置中的连接信息。
另外,SQLite能正常工作是因为SQLite驱动对未绑定连接的模型有兼容逻辑,而asyncpg驱动的校验更严格,直接抛出了未配置连接的错误。
问题2:确保所有模型设置default_connection的方案
方案1:延迟Tortoise初始化,先收集所有模型再初始化
如果插件化系统支持先收集所有需要加载的模型模块路径,再调用Tortoise.init(),这是最稳妥的方式。比如在所有插件加载完毕后,将所有模型的模块路径汇总到self.models中,再执行初始化操作。
方案2:动态加载模型后手动注册到Tortoise
对于Tortoise初始化之后才加载的模型,可以手动调用内部方法完成注册:
from tortoise import Tortoise # 假设PingModel是刚动态加载的模型 await Tortoise._init_model(PingModel, "models", "default")
其中"models"是你配置中apps里的应用名称,"default"是连接名称,要和db_config中的配置完全对应。
方案3:给模型添加Meta类指定连接
如果之前写法有误,正确的Meta类配置如下:
class PingModel(Model): uid = fields.TextField() ping_count = fields.IntField() def __str__(self): return self.name class Meta: default_connection = "default"
注意要确保"default"和配置中的连接名称一致,且该模型要在Tortoise初始化时被扫描到,或者后续手动注册。
方案4:修正动态加载逻辑,确保模型模块可被识别
如果直接指定模型类路径提示模块未找到,说明插件化加载逻辑未将模型所在模块添加到Python的sys.path,或者模块名称写法错误(比如应该是"fishbot.cogs.ping.PingModel"而非"fishbot.cogs.ping")。可以检查模块路径正确性,或在加载插件时将插件所在目录添加到sys.path。
内容的提问来源于stack exchange,提问作者robbmanes

