Tortoise-ORM的模型

Tortoise-ORM 的模型是数据库表的「对象映射载体」,每个模型对应数据库中的一张表,模型中的字段对应表中的一列,模型的实例对应表中的一行数据。通过定义模型,可快速实现数据库表结构的抽象,无需手动编写 DDL 语句,后续结合 Aerich 迁移工具,即可同步表结构到数据库。

一、模型定义基础

Tortoise-ORM 模型的定义遵循固定规范,核心是「继承基类 + 定义字段 + 配置元数据」,具体要求如下:

  1. 所有模型必须继承 tortoise.models.Model 基类(这是 Tortoise 识别模型的核心标识);
  2. 模型中的每个类属性,对应数据库表中的一列,需使用 Tortoise 提供的「字段类型」(如 CharFieldIntField)定义;
  3. 通过「字段参数」(如 max_lengthunique)指定字段的约束(如长度、唯一性)和行为(如默认值);
  4. 可选定义 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.0gt=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 二进制数据(适用于存储图片、文件等二进制内容) 无常用参数,不支持过滤、更新操作,慎用

四、字段参数详解

字段参数用于控制字段的约束规则、默认行为和附加属性,几乎所有字段都支持通用参数,部分字段有专属参数(如 DecimalFieldmax_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 迁移工具生成迁移文件,再应用到数据库,禁止手动修改表结构。
posted @ 2026-02-04 16:14  向闲而过  阅读(0)  评论(0)    收藏  举报