#Tortoise-ORM 快速入门:更符合 Python 直觉的异步数据库操作
📂 所属阶段:第三阶段 — 数据持久化(数据库篇)
🔗 相关章节:SQLAlchemy 2.0 实战 · Redis 集成 · FastAPI 路由
#1. 为什么选择 Tortoise-ORM?
#1.1 对比 SQLAlchemy
| 特性 | Tortoise-ORM | SQLAlchemy |
|---|---|---|
| API 设计 | 更 Pythonic,类似 Django ORM | 更底层,灵活但学习曲线陡 |
| 异步 | 原生异步,开箱即用 | 2.0 才原生支持异步 |
| 配置 | 字典式配置,简单直观 | 需要构建引擎和会话 |
| 迁移 | 需要配合别的工具 | Alembic 官方支持 |
| 适用 | 中小型项目、快速原型 | 大型企业级项目 |
#1.2 项目依赖
pip install tortoise-orm[asyncpg] asyncpg
# asyncpg → PostgreSQL(生产)
# aiosqlite → SQLite(开发):pip install tortoise-orm[sqlite]#2. 配置与初始化
#2.1 基础配置
# tortoise_config.py
from tortoise import Tortoise, fields
from tortoise.fields import ForeignKeyField, ManyToManyField
TORTOISE_ORM = {
"connections": {
"default": {
"engine": "tortoise.backends.sqlite", # 开发
# "engine": "tortoise.backends.asyncpg", # 生产用 PostgreSQL
"credentials": {
"file_path": "./data.db", # SQLite 文件路径
# "host": "localhost",
# "port": 5432,
# "user": "admin",
# "password": "secret",
# "database": "mydb",
}
}
},
"apps": {
"models": {
"models": ["__main__", "models.article"], # 模型所在模块
"default_connection": "default",
}
},
"use_tz": False,
"timezone": "Asia/Shanghai",
}#2.2 在 FastAPI 中初始化
# main.py
from fastapi import FastAPI
from tortoise.contrib.fastapi import register_tortoise
app = FastAPI()
# 方式一:register_tortoise 自动注册
register_tortoise(
app,
db_url="sqlite://./data.db",
modules={"models": ["__main__", "models.article"]},
generate_schemas=True, # 首次运行自动建表
add_exception_handlers=True, # 自动添加 CRUD 异常处理
)
# 方式二:手动初始化(更灵活)
@app.on_event("startup")
async def init():
await Tortoise.init(config=TORTOISE_ORM)
await Tortoise.generate_schemas()
@app.on_event("shutdown")
async def close():
await Tortoise.close_connections()#3. 模型定义
#3.1 模型基础
from tortoise import fields
from tortoise.models import Model
class Category(Model):
id = fields.IntField(pk=True, autoincrement=True)
name = fields.CharField(max_length=50, unique=True)
description = fields.TextField(null=True)
created_at = fields.DatetimeField(auto_now_add=True)
class Meta:
table = "categories"
def __str__(self):
return self.name
class Article(Model):
id = fields.IntField(pk=True)
title = fields.CharField(max_length=200)
content = fields.TextField()
views = fields.IntField(default=0)
published = fields.BooleanField(default=False)
created_at = fields.DatetimeField(auto_now_add=True)
updated_at = fields.DatetimeField(auto_now=True)
# 关联关系
author: fields.ForeignKeyRelation["Author"] = fields.ForeignKeyField(
"models.author", related_name="articles"
)
tags: fields.ManyToManyRelation["Tag"] = fields.ManyToManyField(
"models.tag", related_name="articles", through="article_tag"
)
class Meta:
table = "articles"
ordering = ["-created_at"]
def __str__(self):
return self.title
class Tag(Model):
id = fields.IntField(pk=True)
name = fields.CharField(max_length=30, unique=True)
articles: fields.ManyToManyRelation[Article]
def __str__(self):
return self.name#3.2 支持的字段类型
| 字段 | 说明 | 示例 |
|---|---|---|
IntField | 整数 | id = fields.IntField(pk=True) |
CharField | 字符串 | name = fields.CharField(max_length=100) |
TextField | 长文本 | content = fields.TextField() |
BoolField | 布尔 | active = fields.BooleanField(default=True) |
DatetimeField | 日期时间 | created = fields.DatetimeField(auto_now_add=True) |
DateField | 日期 | birthday = fields.DateField() |
FloatField | 浮点数 | price = fields.FloatField() |
DecimalField | 精确小数 | price = fields.DecimalField(max_digits=10, decimal_places=2) |
JSONField | JSON 数据 | config = fields.JSONField() |
ForeignKeyField | 外键 | author = fields.ForeignKeyField("models.Author") |
ManyToManyField | 多对多 | tags = fields.ManyToManyField("models.Tag") |
#4. CRUD 操作
#4.1 创建(Create)
# 创建单条
article = await Article.create(
title="FastAPI 入门",
content="这是一篇关于 FastAPI 的教程...",
author_id=1,
)
article.published = True
await article.save()
# 批量创建
await Article.bulk_create([
Article(title="文章1", content="内容1", author_id=1),
Article(title="文章2", content="内容2", author_id=1),
])
# 或使用字典创建
await Article.create(title="文章3", content="内容3", author_id=1)#4.2 读取(Read)
# 查询单条
article = await Article.get(id=1)
article = await Article.get_or_none(id=999) # 不存在返回 None,不会抛异常
# 查询多条
articles = await Article.filter(published=True)
articles = await Article.filter(author_id=1, published=True)
articles = await Article.filter(tags__name="Python") # 跨关联查询
# 分页
articles = await Article.all().offset(0).limit(10)
# 排序
articles = await Article.all().order_by("-created_at") # 降序
# 预加载关联
articles = await Article.all().prefetch_related("author", "tags")
# 使用 .only() 指定字段
titles = await Article.all().only("id", "title")
# 统计
count = await Article.filter(published=True).count()
# 是否存在
exists = await Article.exists(id=1)
# 聚合
from tortoise.functions import Count, Max, Avg
from tortoise.aggregations import Sum
# 按作者统计文章数
from tortoise.models import Q
stats = await Article.all().annotate(
article_count=Count("id"),
).group_by("author_id")#4.3 更新(Update)
# 单条更新
article = await Article.get(id=1)
article.title = "新标题"
await article.save()
# 批量更新
await Article.filter(published=False).update(published=True)
# 使用 Q 对象组合条件
from tortoise.models import Q
await Article.filter(Q(views__gt=100) | Q(published=True)).update(hot=True)#4.4 删除(Delete)
# 单条删除
article = await Article.get(id=1)
await article.delete()
# 批量删除
await Article.filter(published=False).delete()#5. 关联查询
#5.1 外键查询
# 正向查询(从文章查作者)
article = await Article.get(id=1).prefetch_related("author")
print(article.author.name)
# 反向查询(从作者查所有文章)
author = await Author.get(id=1).prefetch_related("articles")
for article in author.articles:
print(article.title)#5.2 多对多查询
# 给文章添加标签
article = await Article.get(id=1)
tag = await Tag.get(name="Python")
await article.tags.add(tag)
# 获取文章的所有标签
article = await Article.get(id=1).prefetch_related("tags")
for tag in article.tags:
print(tag.name)
# 获取某标签下的所有文章
tag = await Tag.get(name="Python").prefetch_related("articles")
for article in tag.articles:
print(article.title)
# 移除关联
await article.tags.remove(tag)#6. Tortoise-ORM 专用路由依赖
#6.1 注入当前用户模型
from tortoise.contrib.fastapi import tortoise_app_context
# Tortoise ORM 的中间件,为每个请求创建数据库连接
@app.middleware("http")
async def db_context_middleware(request: Request, call_next):
async with tortoise_app_context():
response = await call_next(request)
return response#6.2 完整 CRUD 路由示例
from fastapi import FastAPI, HTTPException, Query
from tortoise.contrib.fastapi import HTTPNotFoundError
from pydantic import BaseModel
from typing import Optional, List
app = FastAPI()
# Pydantic Schema
class ArticleIn(BaseModel):
title: str
content: str
author_id: int
class ArticleOut(BaseModel):
id: int
title: str
content: str
author_id: int
class Config:
from_attributes = True
# ── CRUD 路由 ────────────────────────────────────
@app.post("/articles", response_model=ArticleOut, status_code=201)
async def create_article(data: ArticleIn):
article = await Article.create(**data.model_dump())
return article
@app.get("/articles", response_model=List[ArticleOut])
async def list_articles(
skip: int = Query(0, ge=0),
limit: int = Query(10, ge=1, le=100),
published: Optional[bool] = None,
):
query = Article.all()
if published is not None:
query = query.filter(published=published)
articles = await query.offset(skip).limit(limit).order_by("-created_at")
return articles
@app.get("/articles/{article_id}", response_model=ArticleOut)
async def get_article(article_id: int):
article = await Article.get_or_none(id=article_id)
if not article:
raise HTTPException(status_code=404, detail="文章不存在")
return article
@app.put("/articles/{article_id}", response_model=ArticleOut)
async def update_article(article_id: int, data: ArticleIn):
article = await Article.get_or_none(id=article_id)
if not article:
raise HTTPException(status_code=404, detail="文章不存在")
await article.update_from_dict(data.model_dump())
await article.save()
return article
@app.delete("/articles/{article_id}", status_code=204)
async def delete_article(article_id: int):
article = await Article.get_or_none(id=article_id)
if not article:
raise HTTPException(status_code=404, detail="文章不存在")
await article.delete()#7. 小结
# Tortoise-ORM 五分钟速查
# 初始化
await Tortoise.init(config=TORTOISE_ORM)
await Tortoise.generate_schemas()
# 创建
obj = await Model.create(field=value)
# 查询
obj = await Model.get(id=1)
objs = await Model.filter(field=value).prefetch_related("relation")
# 更新
obj.field = new_value
await obj.save()
await Model.filter(id=1).update(field=new_value)
# 删除
await obj.delete()
await Model.filter(id=1).delete()💡 选择建议:如果你喜欢 Django ORM 的风格、需要快速开发中小型项目,Tortoise-ORM 是绝佳选择。如果你在做大型企业级系统、需要精细的 SQL 控制,SQLAlchemy 2.0 更合适。
🔗 扩展阅读

