title: Tortoise-ORM与FastAPI集成:异步模型定义与实践
date: 2025/04/20 11:38:23
updated: 2025/04/20 11:38:23
author: cmdragon

excerpt:
Tortoise-ORM通过类继承方式定义数据模型,每个模型类对应数据库中的一张表。模型字段类型与数据库类型自动映射,支持主键、唯一约束、索引等配置。模型间通过外键建立关联,支持异步查询和CRUD操作。FastAPI集成时,通过register_tortoise初始化数据库连接,并结合Pydantic模型实现数据验证。常见错误包括字段验证失败和数据库连接超时,可通过中间件和连接池配置解决。

categories:

  • 后端开发
  • FastAPI

tags:

  • Tortoise-ORM
  • FastAPI
  • 异步数据库
  • 模型定义
  • 数据库配置
  • CRUD接口
  • 错误处理

<img src="https://static.shutu.cn/shutu/jpeg/open44/2025/04/20/6bcd1d8ab9b0c64e8893ca8577be2c2d.jpeg" title="cmdragon_cn.png" alt="cmdragon_cn.png"/>

<img src="https://static.amd794.com/blog/images/cmdragon_cn.png" title="cmdragon_cn.png" alt="cmdragon_cn.png"/>

扫描二维码关注或者微信搜一搜:编程智域 前端至全栈交流与成长

探索数千个预构建的 AI 应用,开启你的下一个伟大创意

第一章 Tortoise-ORM异步模型定义基础

1.1 模型类创建方法

在FastAPI项目中,数据模型是连接业务逻辑与数据库的核心枢纽。Tortoise-ORM采用类继承方式定义模型,每个模型类对应数据库中的一张表。以下是用户模型的完整示例:

from tortoise.models import Model
from tortoise import fields


class User(Model):
    id = fields.IntField(pk=True)  # 主键字段,自动递增
    username = fields.CharField(max_length=50, unique=True)  # 唯一用户名
    email = fields.CharField(max_length=100, index=True)  # 带索引的邮箱字段
    created_at = fields.DatetimeField(auto_now_add=True)  # 自动记录创建时间
    is_active = fields.BooleanField(default=True)  # 账户激活状态
    credit = fields.DecimalField(max_digits=10, decimal_places=2, default=0.0)  # 精确数值存储

    class Meta:
        table = "auth_users"  # 自定义表名
        ordering = ["-created_at"]  # 默认排序规则

该模型在数据库中会生成如下结构的表(以PostgreSQL为例):

CREATE TABLE auth_users
(
    id         SERIAL PRIMARY KEY,
    username   VARCHAR(50)    NOT NULL UNIQUE,
    email      VARCHAR(100)   NOT NULL,
    created_at TIMESTAMP      NOT NULL,
    is_active  BOOLEAN        NOT NULL,
    credit     NUMERIC(10, 2) NOT NULL
);

1.2 字段类型映射原理

Tortoise-ORM的字段系统实现了Python类型与数据库类型的智能转换。当我们执行数据库迁移时,ORM会自动根据模型字段类型生成对应的DDL语句:

Python字段类型PostgreSQL类型MySQL类型SQLite类型
CharFieldVARCHARVARCHARTEXT
UUIDFieldUUIDCHAR(36)TEXT
DatetimeFieldTIMESTAMPDATETIME(6)TEXT
JSONFieldJSONBJSONTEXT
FloatFieldDOUBLE PRECISIONDOUBLEREAL

特殊的字段参数:

  • auto_now_add=True:仅在对象创建时记录时间
  • auto_now=True:每次保存对象时更新时间
  • description='字段说明':生成数据库注释
  • db_index=True:创建独立索引(比index参数更灵活)

1.3 模型关联配置

关联关系配置是ORM的核心功能之一。我们通过外键字段建立模型间的关联:

class Author(Model):
    name = fields.CharField(max_length=100)


class Book(Model):
    title = fields.CharField(max_length=200)
    author = fields.ForeignKeyField(
        'models.Author',
        related_name='books',
        on_delete=fields.CASCADE
    )
    published_date = fields.DateField()

关联查询示例:

# 获取作者及其所有书籍
author = await Author.filter(name="J.K. Rowling").prefetch_related('books')

# 创建关联对象
await Book.create(
    title="Harry Potter and the Philosopher's Stone",
    author=author,
    published_date=date(1997, 6, 26)
)

第二章 FastAPI集成实践

2.1 数据库配置

在FastAPI启动配置中初始化数据库连接:

from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise

app = FastAPI()

DB_CONFIG = {
    "connections": {
        "default": "postgres://user:password@localhost:5432/mydb"
    },
    "apps": {
        "models": {
            "models": ["models"],
            "default_connection": "default",
        }
    },
    "use_tz": True,  # 启用时区支持
    "timezone": "Asia/Shanghai"
}

register_tortoise(
    app,
    config=DB_CONFIG,
    generate_schemas=True,  # 自动生成表结构
    add_exception_handlers=True  # 启用ORM异常处理
)

2.2 路由与模型结合

创建完整的CRUD接口示例:

from pydantic import BaseModel
from fastapi import APIRouter

router = APIRouter()


class UserCreate(BaseModel):
    username: str
    email: str


@router.post("/users")
async def create_user(user: UserCreate):
    db_user = await User.create(**user.dict())
    return {
        "id": db_user.id,
        "created_at": db_user.created_at.isoformat()
    }


@router.get("/users/{user_id}")
async def get_user(user_id: int):
    user = await User.get_or_none(id=user_id).values(
        "username", "email", "created_at")
    return user or {"error": "User not found"}

第三章 课后Quiz

问题1:如何设置UUID主键?

A) id = fields.UUIDField()
B) id = fields.UUIDField(pk=True)
C) id = fields.UUIDField(primary_key=True)

正确答案:C
解析:在Tortoise-ORM中,设置主键需要显式指定primary_key参数。虽然pk是常用的快捷参数,但UUIDField必须使用完整的primary_key=True才能正确生成主键约束。

问题2:异步查询的优势包括?

A) 减少内存占用
B) 避免阻塞事件循环
C) 提高CPU利用率

正确答案:B
解析:异步查询允许事件循环在等待数据库响应时处理其他任务,特别适合高并发的I/O密集型场景。内存占用和CPU利用率主要与程序实现方式相关,并非异步的直接优势。

第四章 常见报错解决方案

4.1 字段验证失败(422错误)

错误示例:

{
  "detail": [
    {
      "loc": [
        "body",
        "username"
      ],
      "msg": "ensure this value has at most 50 characters",
      "type": "value_error.any_str.max_length"
    }
  ]
}

解决方法:

  1. 检查请求数据是否符合模型约束
  2. 在Pydantic模型中设置相同的验证规则
  3. 使用中间件捕获验证异常:
from fastapi.exceptions import RequestValidationError


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request, exc):
    return JSONResponse(
        status_code=400,
        content={"detail": exc.errors()},
    )

4.2 数据库连接超时

错误信息:
DBConnectionError: Can't connect to MySQL server on 'localhost'

排查步骤:

  1. 验证数据库服务是否正常运行
  2. 检查连接字符串格式:dialect://user:password@host:port/dbname
  3. 增加连接池配置:
DB_CONFIG = {
    "connections": {
        "default": {
            "engine": "tortoise.backends.asyncpg",
            "credentials": {
                "host": "localhost",
                "port": "5432",
                "database": "mydb",
                "user": "user",
                "password": "password",
                "minsize": 3,  # 最小连接数
                "maxsize": 20  # 最大连接数
            }
        }
    }
}

余下文章内容请点击跳转至 个人博客页面 或者 扫码关注或者微信搜一搜:编程智域 前端至全栈交流与成长,阅读完整的文章:
Tortoise-ORM与FastAPI集成:异步模型定义与实践 | cmdragon's Blog

往期文章归档:


风流倜傥的伤痕
79 声望23 粉丝