tortoiseorm不能直接用basemodel继承,因其未提供该基类,必须显式继承tortoise.models.model并用fields模块声明字段;初始化需在事件循环启动前调用init_db(),外键和多对多关系须严格匹配apps配置路径,序列化需用.values()等方法避免json错误。

为什么 TortoiseORM 不能直接用 BaseModel 继承?
很多人一上来就写 class User(BaseModel),结果报错 NameError: name 'BaseModel' is not defined。Tortoise 不像 SQLAlchemy 或 Pydantic 那样提供通用基类,它要求模型必须显式继承 tortoise.models.Model,且需配合 fields 模块声明字段。
- 正确写法是:先
from tortoise import models, fields,再class User(models.Model) -
id字段默认由fields.IntField(pk=True)或更推荐的fields.UUIDField(pk=True)显式定义(避免整型 ID 泄露业务量) - 不支持
__annotations__自动推导(如name: str),所有字段必须用fields.CharField(max_length=255)等显式声明
init_db() 必须在事件循环启动前调用,否则 await 报错
常见错误是把 await Tortoise.init(...) 放在 async def main() 里,再用 asyncio.run(main()) —— 这会导致连接池未初始化就尝试执行 .all(),抛出 RuntimeError: No current event loop in thread 或 Tortoise is not initialized。
- 正确顺序:先
await Tortoise.init(),再await Tortoise.generate_schemas()(开发期可选),最后才跑业务逻辑 - 若用 FastAPI,应在
startup事件中调用init,而非路由函数内 - 配置里
connections的default键名必须与模型Meta中的app值一致(默认是"models",不是"default")
ForeignKey 和 ManyToManyField 怎么写才不出错?
外键字段名和关联模型路径容易混淆。比如想让 Post 关联 User,写成 author = fields.ForeignKeyField('models.User', related_name='posts') —— 这里的 'models.User' 是模块路径,不是类名,且必须和 Tortoise.init(..., apps={'models': [...]} ) 中的 app 名匹配。
-
related_name是反向查询字段名,比如user.posts.all(),不能重复;同一模型多个外键需设不同related_name - 多对多必须用中间表模型(Tortoise 不支持隐式中间表),且两端都要定义
ManyToManyField,例如tags = fields.ManyToManyField('models.Tag', related_name='posts') - 删除时级联行为靠
on_delete控制:fields.CASCADE、fields.SET_NULL(后者要求字段null=True)
异步查询返回的是模型实例,不是 dict,别直接 JSON.dumps
await User.all() 返回的是 List[User],每个 User 是带异步方法的实例,直接传给 json.dumps 会报 TypeError: Object of type User is not JSON serializable。
- 安全序列化用
.values()(返回 list of dict)或.values_list('id', 'name') - 需要完整字段 + 关联数据时,用
.prefetch_related('posts')配合.values(),但注意prefetch_related不支持嵌套三层以上 - 若坚持用模型实例,可重写
pydantic_model_creator(User)生成 Pydantic 模型,但需额外安装pydantic并注意字段类型映射差异(如UUIDField→str)
apps 配置键名和模型内 Meta.app 的一致性——这是最常被忽略、又最难 debug 的地方。Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











