Tortoise-ORM的模型
Tortoise-ORM 的模型是数据库表的「对象映射载体」,每个模型对应数据库中的一张表,模型中的字段对应表中的一列,模型的实例对应表中的一行数据。通过定义模型,可快速实现数据库表结构的抽象,无需手动编写 DDL 语句,后续结合 Aerich 迁移工具,即可同步表结构到数据库。
一、模型定义基础
Tortoise-ORM 模型的定义遵循固定规范,核心是「继承基类 + 定义字段 + 配置元数据」,具体要求如下:
- 所有模型必须继承
tortoise.models.Model基类(这是 Tortoise 识别模型的核心标识); - 模型中的每个类属性,对应数据库表中的一列,需使用 Tortoise 提供的「字段类型」(如
CharField、IntField)定义; - 通过「字段参数」(如
max_length、unique)指定字段的约束(如长度、唯一性)和行为(如默认值); - 可选定义
Meta内部类,配置模型的元数据(如表名、排序规则、索引等),用于优化表结构和查询行为。
二、基本模型示例
以下是一个完整的 User 模型示例,涵盖模型定义的所有基础结构,可直接复制到项目中使用(需确保已导入对应模块):
# 1. 导入核心模块:基类 Model 和字段类型
from tortoise.models import Model
from tortoise import fields
# 2. 定义模型(继承 Model 基类),对应数据库中的 auth_users 表
class User(Model):
# 字段定义:每个字段对应表中的一列
id = fields.IntField(pk=True) # 主键字段,整数类型,自动递增(主键必选)
username = fields.CharField(max_length=50, unique=True) # 用户名,字符串,最大长度50,唯一约束
email = fields.CharField(max_length=100, index=True) # 邮箱,字符串,最大长度100,添加索引(提升查询速度)
created_at = fields.DatetimeField(auto_now_add=True) # 创建时间,自动记录首次保存时的当前时间
is_active = fields.BooleanField(default=True) # 账户激活状态,布尔值,默认值为 True(激活)
credit = fields.DecimalField(
max_digits=10,
decimal_places=2,
default=0.0
) # 信用值,高精度小数,最大10位数字(含2位小数),默认值0.0
# 3. 元数据配置(可选,控制表的额外属性)
class Meta:
table = "auth_users" # 自定义数据库表名(默认会将模型名转为小写复数,此处手动指定)
ordering = ["-created_at"] # 默认查询排序:按创建时间倒序(-表示倒序)
示例核心说明
pk=True:标记该字段为主键,主键是表中唯一标识每一行数据的字段,通常只需要一个主键;IntField(pk=True)会自动设置为「自增整数」,无需手动赋值。auto_now_add=True:仅用于DatetimeField/DateField,表示字段值在「首次保存模型实例」时自动填充为当前时间,后续修改实例不会更新该字段。default:设置字段的默认值,当创建模型实例时未传入该字段值,会自动使用默认值填充。Meta类:若不定义,Tortoise 会使用默认配置(表名=模型名小写复数,无默认排序),建议手动配置table字段,避免默认表名不符合业务规范。
三、核心字段类型详解
Tortoise-ORM 提供了多种字段类型,每种类型对应数据库中的一种列类型,需根据业务需求选择。以下是最常用的字段类型,整理为表格便于查阅,包含其对应数据库类型、描述及常用参数:
| Python 字段类型 | 对应数据库类型 | 描述 | 常用参数 |
|---|---|---|---|
CharField |
VARCHAR | 短字符串(适用于用户名、标题等长度固定的文本) | max_length(必填,最大长度)、unique=True(唯一)、index=True(索引)、null=True(允许为空) |
TextField |
TEXT | 长文本(适用于内容、描述等长度不固定的大文本) | null=True(允许为空)、description(字段描述) |
IntField |
INTEGER | 普通整数(适用于ID、数量等无需大范围的整数) | pk=True(主键)、default=0(默认值)、gt=0(值大于0) |
BigIntField |
BIGINT | 长整数(适用于大范围整数,如时间戳、大数量) | 同 IntField |
FloatField |
DOUBLE/REAL | 浮点数(适用于无需高精度的小数,如评分、比例) | default=0.0、gt=0(值大于0) |
DecimalField |
DECIMAL | 高精度小数(适用于金额、信用值等需精确计算的场景) | max_digits(必填,总位数)、decimal_places(必填,小数位数)、default=0.00 |
BooleanField |
BOOLEAN | 布尔值(适用于状态、开关等二选一的场景) | default=True/False(默认值) |
DateField |
DATE | 日期(仅包含年、月、日,如 2024-05-20) | auto_now=True(每次保存更新)、default(默认日期) |
DatetimeField |
DATETIME/TIMESTAMP | 日期时间(包含年、月、日、时、分、秒,如 2024-05-20 14:30:00) | auto_now_add=True(首次保存自动填充)、auto_now=True(每次保存更新) |
JSONField |
JSON/JSONB | JSON 格式(适用于存储字典、列表等非结构化数据) | encoder(自定义编码器)、decoder(自定义解码器)、null=True |
UUIDField |
UUID/CHAR(36) | 唯一标识符(适用于分布式系统中的主键,避免ID冲突) | pk=True(作为主键时,默认生成 UUID4 格式) |
BinaryField |
BLOB | 二进制数据(适用于存储图片、文件等二进制内容) | 无常用参数,不支持过滤、更新操作,慎用 |
四、字段参数详解
字段参数用于控制字段的约束规则、默认行为和附加属性,几乎所有字段都支持通用参数,部分字段有专属参数(如 DecimalField 的 max_digits)。以下是最常用的通用参数,按使用频率排序:
4.1 通用核心参数
- max_length(必填,仅
CharField):指定字符串的最大长度,数据库会限制字段值的长度,超过会报错(如用户名最长50个字符)。 - pk:是否将该字段设为主键,
pk=True表示为主键,一个模型只能有一个主键;若不指定主键,Tortoise 会自动生成一个名为id的自增整数主键。 - null:是否允许字段值为空,
null=True表示数据库中该列允许存储NULL值(默认null=False,不允许为空)。 - default:字段的默认值,创建模型实例时未传入该字段值,会自动使用默认值填充(支持固定值,如
default=0,也支持简单函数,如default=datetime.now)。 - unique:是否启用唯一约束,
unique=True表示该字段的值在整个表中必须唯一(如用户名、邮箱,不能重复),重复插入会报错。 - index:是否为该字段创建索引,
index=True会在数据库中为该字段创建索引,大幅提升该字段的查询速度(适用于频繁作为查询条件的字段,如邮箱、手机号)。 - description:字段的描述信息,用于生成数据库注释或 API 文档,不影响字段功能(如
description="用户登录邮箱")。 - validators:自定义验证函数,用于对字段值进行额外校验(如信用值不能为负数、手机号格式正确等)。
4.2 自定义验证函数示例
通过 validators 参数,可自定义字段值的校验规则,不符合规则会抛出异常,阻止数据插入/更新:
from tortoise.models import Model
from tortoise import fields
from tortoise.validators import Validator
# 1. 定义自定义验证函数(校验信用值不能为负数)
def validate_credit(value):
if value < 0:
# 抛出异常,Tortoise 会捕获并返回错误信息
raise ValueError("信用值不能为负数,请输入非负值")
# 2. 定义模型,给 credit 字段添加自定义验证
class User(Model):
id = fields.IntField(pk=True)
username = fields.CharField(max_length=50, unique=True)
# 给 credit 字段添加自定义验证函数
credit = fields.IntField(default=0, validators=[validate_credit])
# 测试:创建实例时传入负数,会触发验证异常
# user = User(username="test", credit=-10)
# await user.save() # 执行会抛出 ValueError: 信用值不能为负数,请输入非负值
五、模型元数据(Meta 类详解)
模型中的 Meta 内部类,用于配置模型的元数据,控制数据库表的额外属性和查询行为,常用属性如下,可根据业务需求组合使用:
5.1 常用 Meta 属性
- table:自定义数据库表名,必填(推荐),若不设置,Tortoise 会默认将模型名转为「小写复数」(如
User模型默认表名为users)。 - ordering:默认查询排序规则,传入字符串列表,字符串前加
-表示倒序,不加表示升序(如ordering=["-created_at", "username"]表示先按创建时间倒序,再按用户名升序)。 - unique_together:定义联合唯一约束,传入字段名列表的列表,表示多个字段组合起来必须唯一(如「活动名称+活动日期」不能重复)。
- indexes:定义复合索引,传入字段名列表的列表,表示为多个字段联合创建索引(如同时按「地点+日期」查询时,提升查询速度)。
- managed:是否由 Tortoise 管理表结构,
managed=True(默认)表示 Tortoise 会通过迁移工具创建/修改表结构;managed=False表示手动管理表结构,Tortoise 不干预。
5.2 Meta 类完整示例
以下示例展示 Meta 类常用属性的组合使用,适配「活动管理」场景:
from tortoise.models import Model
from tortoise import fields
class Event(Model):
# 字段定义
id = fields.IntField(pk=True)
name = fields.CharField(max_length=100, description="活动名称")
location = fields.CharField(max_length=200, description="活动地点")
date = fields.DateField(description="活动日期")
description = fields.TextField(null=True, description="活动描述")
# Meta 类配置
class Meta:
table = "events" # 自定义表名为 events
unique_together = [("name", "date")] # 联合唯一:活动名称 + 活动日期 不能重复
indexes = [("location", "date")] # 复合索引:地点 + 日期,提升联合查询速度
ordering = ["date"] # 默认按活动日期升序排序(先举办的活动在前)
managed = True # 由 Tortoise 管理表结构(默认值,可省略)
六、常见问题与避坑指南
- 问题1:模型未继承 Model 基类,报错「Not a valid Tortoise model」:所有模型必须继承
tortoise.models.Model,缺一不可,检查导入和继承关系。 - 问题2:CharField 未指定 max_length,报错「CharField requires max_length parameter」:
CharField必须指定max_length参数,否则无法生成数据库表(VARCHAR 类型必须有长度限制)。 - 问题3:主键重复,报错「UNIQUE constraint failed」:主键(pk=True)必须唯一,若使用
IntField(pk=True),无需手动赋值(自动自增);若使用UUIDField(pk=True),默认自动生成唯一 UUID。 - 问题4:自定义验证函数不生效:确保验证函数正确抛出异常(如 ValueError),且已将函数传入
validators参数(注意是列表格式,如validators=[validate_credit])。 - 问题5:Meta 类中 ordering 配置无效:ordering 接收「字符串列表」,单个字段也需用列表(如
ordering=["date"]),不能直接写ordering="date"。 - 问题6:字段允许为空,但传入 None 仍报错:需确保
null=True,同时字段没有自定义验证函数禁止 None 值(如验证函数中判断if value is None抛出异常)。
七、核心注意事项
- 模型定义后,需将模型所在模块添加到 Tortoise-ORM 的配置中(
apps.models列表),否则 Aerich 迁移工具无法扫描到模型,无法生成迁移文件。 - 字段类型和参数需与业务需求匹配,如金额必须用
DecimalField(高精度),不能用FloatField(避免精度丢失);非结构化数据用JSONField。 - 索引和唯一约束需合理使用:频繁查询的字段添加索引,需唯一的字段添加 unique 约束,但过多索引会降低插入/更新速度(权衡查询和写入性能)。
- 生产环境中,模型修改后(如新增字段、修改字段类型),必须通过 Aerich 迁移工具生成迁移文件,再应用到数据库,禁止手动修改表结构。

浙公网安备 33010602011771号