> ## Content Index
> Fetch the complete content index at: https://blog.vercanti.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Tortoise ORM 模型字段完全参考
- URL: https://blog.vercanti.com/tortoise-orm-mo-xing-zi-duan-wan-quan-can-kao/
- Published: 2026-08-28T14:34:53.000Z
- Updated: 2026-08-28T14:57:36.000Z
- Description: 以下参数所有字段类型都支持： db_default 可用表达式（需从 tortoise.fields.db_defaults 导入）： 整数，对应数据库 INT（32位有符号，约 -21亿 ~ 21亿）。 大整数，对应数据库 BIGINT（64位）。常用于雪花 ID 等大数值主键。 小整数，对应 SMALLINT（-32768 ~ 32767）。适合状态值、枚举数字。 双精度浮点，对应 DOUBLE。 精确小数，对应 DECIMAL。 对应数据库 VARCHAR(max_length)。 无限长文本，对应 TEXT。不能加 index（大文本不适合索引）
- Author: yellowdog
- Tags: Tortoise-orm

> 官方文档：<https://tortoise.github.io/fields.html>  
> 适用版本：tortoise-orm >= 0.20（2026-05-07 核实）

---

## 通用参数（所有字段共有）

以下参数所有字段类型都支持：

| 参数            | 类型             | 默认值           | 说明                                                                            |
| ------------- | -------------- | ------------- | ----------------------------------------------------------------------------- |
| default       | Any / callable | None          | Python 层默认值。传入可调用对象（如 dict）则每次调用，避免共享可变默认值                                    |
| db\_default   | Expression     | NOT\_PROVIDED | **数据库层面**的默认值表达式（由数据库执行，Python 不传值时生效）。可用 Now()、RandomHex()、SqlDefault(sql) 等 |
| null          | bool           | False         | 是否允许数据库存储 NULL                                                                |
| db\_index     | bool           | None          | 是否为该字段创建数据库索引                                                                 |
| unique        | bool           | False         | 是否添加唯一约束                                                                      |
| primary\_key  | bool           | None          | 是否作为主键（每个模型只能有一个）                                                             |
| source\_field | str            | None          | 数据库列名，None 则使用字段名                                                             |
| description   | str            | None          | 字段描述，会出现在 schema 中                                                            |
| validators    | list           | None          | 验证器列表（在 save() 前运行）                                                           |
| generated     | bool           | False         | 标记为数据库自动生成，不在 INSERT 时提交                                                      |

**`db_default` 可用表达式（需从 `tortoise.fields.db_defaults` 导入）：**

| 表达式             | 说明                         |
| --------------- | -------------------------- |
| Now()           | CURRENT\_TIMESTAMP，数据库当前时间 |
| RandomHex()     | 随机 32 字符十六进制字符串            |
| SqlDefault(sql) | 任意 SQL 表达式字符串              |

---

## 数值类型

### `IntField`

整数，对应数据库 `INT`（32位有符号，约 -21亿 \~ 21亿）。

```python
from tortoise import fields

class Model(Model):
    count = fields.IntField(default=0)
    age = fields.IntField(null=True)

```

### `BigIntField`

大整数，对应数据库 `BIGINT`（64位）。常用于雪花 ID 等大数值主键。

```python
snowflake_id = fields.BigIntField(primary_key=True)

```

### `SmallIntField`

小整数，对应 `SMALLINT`（-32768 \~ 32767）。适合状态值、枚举数字。

### `FloatField`

双精度浮点，对应 `DOUBLE`。

> 注意：金融计算不要用 `FloatField`，使用 `DecimalField`。

### `DecimalField(max_digits, decimal_places, ...)`

精确小数，对应 `DECIMAL`。

| 参数              | 类型  | 必填 | 说明        |
| --------------- | --- | -- | --------- |
| max\_digits     | int | ✅  | 总位数（含小数位） |
| decimal\_places | int | ✅  | 小数点后位数    |

```python
price = fields.DecimalField(max_digits=10, decimal_places=2)
# max_digits=10, decimal_places=2 → 最大 99999999.99

```

---

## 字符串类型

### `CharField(max_length, ...)`

| 参数          | 类型  | 必填 | 说明             |
| ----------- | --- | -- | -------------- |
| max\_length | int | ✅  | 最大字符数（数据库层面限制） |

对应数据库 `VARCHAR(max_length)`。

```python
name = fields.CharField(max_length=100)
username = fields.CharField(max_length=50, unique=True, db_index=True)

```

### `TextField`

无限长文本，对应 `TEXT`。不能加 `index`（大文本不适合索引）。

```python
content = fields.TextField(null=True)

```

---

## 布尔类型

### `BooleanField`

对应 `BOOLEAN` / `TINYINT(1)`。

```python
is_active = fields.BooleanField(default=True)
is_deleted = fields.BooleanField(default=False)

```

---

## 时间日期类型

### `DatetimeField(auto_now=False, auto_now_add=False, ...)`

| 参数             | 类型   | 默认    | 说明                               |
| -------------- | ---- | ----- | -------------------------------- |
| auto\_now      | bool | False | 每次 save() 时自动更新为当前时间（update 时间戳） |
| auto\_now\_add | bool | False | 仅在**创建**时设置为当前时间，之后不再更新          |

```python
class Article(Model):
    created_at = fields.DatetimeField(auto_now_add=True)  # 创建时间
    updated_at = fields.DatetimeField(auto_now=True)      # 更新时间
    published_at = fields.DatetimeField(null=True)        # 手动设置

```

> 注意：`auto_now=True` 的字段自动设置 `editable=False`，手动赋值会被忽略。

### `DateField`

只存日期（无时分秒），对应 `DATE`。不支持 `auto_now` / `auto_now_add` 参数。

### `TimeField(auto_now=False, auto_now_add=False)`

只存时间，对应 `TIME`。参数同 `DatetimeField`。

### `TimeDeltaField`

存储时间间隔，Python 类型为 `datetime.timedelta`，对应 `BIGINT`（微秒数）。

---

## 二进制与特殊类型

### `BinaryField`

存储二进制数据，对应 `BLOB`。Python 类型为 `bytes`。

```python
thumbnail = fields.BinaryField(null=True)

```

### `JSONField`

存储 JSON 数据，对应 `JSON`（MySQL 8+/PostgreSQL）或 `TEXT`（SQLite）。  
Python 类型为 `dict` / `list`，ORM 自动序列化/反序列化。

```python
metadata = fields.JSONField(default=dict)   # 注意：传 dict 而不是 {}
tags = fields.JSONField(default=list)       # 列表默认值

```

> 陷阱：`default={}` 共享同一个字典对象，务必用 `default=dict`。

### `UUIDField`

UUID 字段，Python 类型为 `uuid.UUID`。

```python
import uuid

class Model(Model):
    id = fields.UUIDField(primary_key=True, default=uuid.uuid4)

```

> 使用 UUID 作为主键适合分布式场景，避免自增 ID 暴露信息量。

---

## 关系字段

### `ForeignKeyField(to, related_name=None, on_delete=CASCADE, db_constraint=True, to_field=None, ...)`

| 参数             | 类型           | 默认      | 说明                                      |                                             |
| -------------- | ------------ | ------- | --------------------------------------- | ------------------------------------------- |
| to             | str \| Model | ✅       | 目标模型，格式 'app.ModelName' 或直接传模型类         |                                             |
| related\_name  | str \| None  | False   | None                                    | 反向关系名称；None 自动生成为 <模型名小写>\_set；False 禁用反向访问 |
| on\_delete     | str          | CASCADE | 父记录删除时的行为（见下表）                          |                                             |
| db\_constraint | bool         | True    | 是否在数据库创建外键约束（False 可提升迁移灵活性但失去 DB 级联保障） |                                             |
| to\_field      | str          | None    | 指向目标模型的哪个字段（默认是主键）                      |                                             |

**`on_delete` 可选值：**

| 值                   | 说明                               |
| ------------------- | -------------------------------- |
| fields.CASCADE      | 父记录删除时，子记录一并删除                   |
| fields.RESTRICT     | 有子记录时禁止删除父记录（抛出异常）               |
| fields.SET\_NULL    | 父记录删除时，子字段设为 NULL（需配合 null=True） |
| fields.SET\_DEFAULT | 父记录删除时，子字段设为 default             |
| fields.NO\_ACTION   | 数据库不执行任何操作（由应用层控制）               |

```python
class Student(Model):
    clazz = fields.ForeignKeyField(
        'models.Class',
        related_name='students',      # Class 实例通过 .students 访问关联学生
        on_delete=fields.CASCADE,
        null=True,
    )

```

**访问方式：**

```python
student = await Student.get(id=1).select_related('clazz')
print(student.clazz.name)           # 正向访问（已预加载）

clazz = await Class.get(id=1).prefetch_related('students')
async for s in clazz.students:      # 反向访问集合
    print(s.name)

```

> 注意：未 `select_related` 时，直接访问 `student.clazz` 不会自动查库（异步 ORM 无法隐式发 IO），需要 `await student.clazz` 或先预加载。

### `OneToOneField(to, related_name=None, on_delete=CASCADE, ...)`

参数同 `ForeignKeyField`，但加了唯一约束，确保一对一关系。

```python
class UserProfile(Model):
    user = fields.OneToOneField('models.User', related_name='profile', on_delete=fields.CASCADE)
    bio = fields.TextField(null=True)

```

### `ManyToManyField(to, through=None, forward_key=None, backward_key='', related_name='', on_delete=CASCADE, db_constraint=True, ...)`

| 参数            | 类型           | 默认   | 说明                                        |
| ------------- | ------------ | ---- | ----------------------------------------- |
| to            | str \| Model | ✅    | 目标模型，格式 'app.ModelName' 或直接传模型类           |
| through       | str          | None | 中间表模型；None 则自动创建（app\_\_model1\_\_model2） |
| forward\_key  | str          | None | 中间表中指向**本模型**的外键字段名                       |
| backward\_key | str          | ''   | 中间表中指向**目标模型**的外键字段名                      |
| related\_name | str          | ''   | 目标模型上的反向访问名                               |

```python
class Student(Model):
    courses = fields.ManyToManyField('models.Course', related_name='students')

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

```

**操作 M2M：**

```python
student = await Student.get(id=1)
course = await Course.get(id=2)

await student.courses.add(course)           # 添加关联
await student.courses.remove(course)        # 移除关联
await student.courses.clear()               # 清空所有关联
courses = await student.courses.all()       # 获取所有关联
await student.courses.filter(name='数学')  # 在关联集合中过滤

```

---

## 枚举类型

### `CharEnumField(enum_type, description=None, max_length=0)`

将 Python `str` 枚举映射为数据库 `VARCHAR`，存储枚举的值（`.value`）。

| 参数          | 说明                      |
| ----------- | ----------------------- |
| enum\_type  | str 枚举类（必填）             |
| description | 字段描述                    |
| max\_length | VARCHAR 长度，0 表示自动从枚举值推断 |

```python
from enum import Enum
from tortoise import fields

class Status(str, Enum):
    active = 'active'
    inactive = 'inactive'
    deleted = 'deleted'

class User(Model):
    status = fields.CharEnumField(Status, default=Status.active)

```

### `IntEnumField(enum_type, description=None)`

将 Python `int` 枚举映射为数据库 `INT`，存储枚举的值（`.value`）。

| 参数          | 说明          |
| ----------- | ----------- |
| enum\_type  | int 枚举类（必填） |
| description | 字段描述        |

```python
from enum import IntEnum

class Priority(IntEnum):
    low = 1
    medium = 2
    high = 3

class Task(Model):
    priority = fields.IntEnumField(Priority, default=Priority.medium)

```

> 枚举字段的好处：数据库只存 `'active'` 或 `1` 等原始值，ORM 自动将其转换为对应的枚举对象，防止使用魔法字符串/数字。

---

## 主键字段

### `IntField(primary_key=True)` / `BigIntField(primary_key=True)`

自增整数主键（最常用）。

### `UUIDField(primary_key=True, default=uuid.uuid4)`

UUID 主键，适合分布式场景。

### 自动主键

如果不定义任何 `primary_key=True` 字段，Tortoise 会自动添加 `id = IntField(primary_key=True)`。

---

## 字段验证器

```python
from tortoise.validators import MinValueValidator, MaxValueValidator, RegexValidator

class Product(Model):
    price = fields.DecimalField(
        max_digits=10,
        decimal_places=2,
        validators=[MinValueValidator(0)]
    )
    phone = fields.CharField(
        max_length=20,
        validators=[RegexValidator(r'^\d{11}$', re.I)]
    )

```

内置验证器：

- `MinValueValidator(min_value)` — 数值最小值
- `MaxValueValidator(max_value)` — 数值最大值
- `MinLengthValidator(min_length)` — 字符串最小长度
- `MaxLengthValidator(max_length)` — 字符串最大长度
- `RegexValidator(pattern, flags)` — 正则匹配

自定义验证器：

```python
from tortoise.validators import Validator
from tortoise.exceptions import ValidationError

class PositiveValidator(Validator):
    def __call__(self, value):
        if value <= 0:
            raise ValidationError('值必须为正数')

```

---

## 完整模型示例

```python
import uuid
from tortoise import Model, fields

class User(Model):
    # 主键
    id = fields.IntField(primary_key=True)

    # 基本信息
    username = fields.CharField(max_length=50, unique=True, db_index=True, description='用户名')
    email = fields.CharField(max_length=254, unique=True)
    password_hash = fields.CharField(max_length=128)
    avatar = fields.CharField(max_length=500, null=True)
    bio = fields.TextField(null=True)

    # 状态
    is_active = fields.BooleanField(default=True)
    is_admin = fields.BooleanField(default=False)

    # 时间
    created_at = fields.DatetimeField(auto_now_add=True)
    updated_at = fields.DatetimeField(auto_now=True)
    last_login = fields.DatetimeField(null=True)

    # 扩展数据
    extra = fields.JSONField(default=dict)

    class Meta:
        table = 'user'           # 指定表名（默认为模型名小写）
        ordering = ['-created_at']  # 默认排序

    def __str__(self):
        return self.username

```

---

## `class Meta` 配置

| 属性                 | 说明                               |
| ------------------ | -------------------------------- |
| table              | 指定数据库表名                          |
| ordering           | 默认排序字段列表，\-field 表示降序            |
| unique\_together   | 联合唯一约束，如 (('field1', 'field2'),) |
| indexes            | 联合索引，如 (('field1', 'field2'),)   |
| table\_description | 表注释                              |
| manager            | 自定义 Manager 类                    |

```python
class Meta:
    table = 'user_profile'
    ordering = ['-created_at']
    unique_together = (('user_id', 'date'),)  # 联合唯一
    indexes = (('status', 'created_at'),)     # 联合索引

```

---

## 常见踩坑

1. **`JSONField(default={})` 陷阱**：多个实例共享同一个字典对象，改一个会影响所有。应用 `default=dict`（传可调用对象）。
2. **未 `await` 关系字段**：异步 ORM 中访问未加载的关系会报错，需要提前 `select_related` 或 `prefetch_related`。
3. **`auto_now=True` 字段无法手动设置**：包含该字段的字典在 `update()` 时会被自动覆盖。
4. **`CharField` 不能不指定 `max_length`**：这是必填参数，否则报错。
5. **`on_delete=SET_NULL` 必须配 `null=True`**：否则数据库约束冲突。

---

## 最佳实践

**可变类型默认值用可调用对象，不用字面量**：`JSONField(default={})` 或 `JSONField(default=[])` 会让所有实例共享同一对象，导致数据污染。应传入函数引用。

```python
# 错误
tags = fields.JSONField(default={})

# 正确
tags = fields.JSONField(default=dict)
metadata = fields.JSONField(default=list)

```

**主键用 `UUIDField(pk=True)` 替代自增 ID（分布式场景）**：自增 ID 在分布式场景下难以合并，UUID 可在应用层生成，无需数据库往返。

```python
import uuid
class Order(Model):
    id = fields.UUIDField(pk=True, default=uuid.uuid4)

```

**金额字段必须用 `DecimalField`，不用 `FloatField`**：浮点数存在精度问题，金融计算应使用精确小数类型。

```python
price = fields.DecimalField(max_digits=12, decimal_places=2)

```

**时间字段统一用带时区的类型**：`DatetimeField(auto_now_add=True)` 在 UTC 时区存储，业务层做时区转换，避免混用本地时间。

**关联字段 `on_delete` 必须明确指定**：不同业务语义对应不同策略，不要依赖默认值，显式声明避免歧义。

```python
author = fields.ForeignKeyField('models.User', on_delete=fields.CASCADE)
category = fields.ForeignKeyField('models.Category', on_delete=fields.SET_NULL, null=True)

```

---

## 常见陷阱

### 陷阱：访问关系字段时抛出 `NoValuesFetched`

**现象：** 访问 `instance.author` 或 `instance.tags` 时抛出 `tortoise.exceptions.NoValuesFetched`。

**原因：** Tortoise ORM 的关系字段是懒加载的，查询时不自动 JOIN，必须显式预加载后才能访问。

**解决：** 在查询时使用 `prefetch_related` / `select_related`，或之后调用 `fetch_related`。

```python
# 错误：直接访问未加载的关系
post = await Post.get(id=1)
print(post.author.name)  # NoValuesFetched

# 正确方式一：查询时预加载
post = await Post.get(id=1).prefetch_related('author')

# 正确方式二：事后加载
await post.fetch_related('author')
print(post.author.name)

```

### 陷阱：`auto_now` 字段在 `update()` 时不更新

**现象：** 使用 `Model.filter(...).update(status='done')` 后，`updated_at` 字段没有变化。

**原因：** `auto_now=True` 只在 `instance.save()` 时生效（由 Python 层在 save 前赋值），批量 `update()` 直接执行 SQL，不经过 Python 层，所以不触发 `auto_now`。

**解决：** 批量更新时显式传入时间字段，或改用逐条 `save()`。

```python
from datetime import datetime, timezone

await Post.filter(status='draft').update(
    status='published',
    updated_at=datetime.now(timezone.utc),  # 显式传入
)

```

### 陷阱：`SET_NULL` 与 `null=False` 组合导致数据库约束冲突

**现象：** 删除关联记录时抛出数据库约束错误，或迁移时报错。

**原因：** `on_delete=SET_NULL` 要求数据库将外键列设为 NULL，但若该列未声明 `null=True`，数据库会拒绝。

**解决：** `on_delete=SET_NULL` 必须同时设置 `null=True`。

```python
# 错误
category = fields.ForeignKeyField('models.Category', on_delete=fields.SET_NULL)

# 正确
category = fields.ForeignKeyField('models.Category', on_delete=fields.SET_NULL, null=True)

```

---

## 参见

- [查询操作完全指南](https://blog.vercanti.com/tortoise-orm-cha-xun-cao-zuo-wan-quan-zhi-nan/)
- [事务与并发](https://blog.vercanti.com/tortoise-orm-shi-wu-yu-bing-fa/)
- [信号机制](https://blog.vercanti.com/xin-hao-ji-zhi/)
- [初始化与配置](https://blog.vercanti.com/tortoise-orm-chu-shi-hua-yu-pei-zhi/)