framework
framework/tortoise/model-class-pattern.md
medium
tortoise-model-pattern
Tortoise Model 类标准模板 — Meta.table + auto_now/auto_now_add。Use when 写 Python 后端代码 / 评审涉及 `model-class-pattern` 的 PR。
tortoisemodelfields.meta类标准模板
paths
backend/models/**/*.pypy/models/**/*.py
Tortoise · Model 类模板
标准结构
# py/models/referral.py
from tortoise import fields
from tortoise.models import Model
from models.base import TimestampMixin
class Referral(TimestampMixin, Model):
"""邀请关系(一对一:邀请人 → 被邀请人)"""
id = fields.UUIDField(pk=True)
inviter_session_id = fields.UUIDField(index=True)
invitee_session_id = fields.UUIDField(unique=True)
invite_code = fields.CharField(max_length=16, index=True)
class Meta:
table = "referrals"
unique_together = (("inviter_session_id", "invitee_session_id"),)
def __str__(self) -> str:
return f"<Referral {self.invite_code}>"
TimestampMixin
# py/models/base.py
from tortoise import fields
class TimestampMixin:
created_at = fields.DatetimeField(auto_now_add=True)
updated_at = fields.DatetimeField(auto_now=True)
字段类型对照
| Tortoise | SQL(MySQL 8) |
|---|---|
IntField(pk=True, generated=True) |
INT AUTO_INCREMENT PRIMARY KEY |
UUIDField(pk=True) |
CHAR(36) PRIMARY KEY |
CharField(max_length=N) |
VARCHAR(N) |
TextField() |
TEXT |
JSONField() |
JSON |
DatetimeField(auto_now_add=True) |
DATETIME(6) DEFAULT CURRENT_TIMESTAMP(6) |
BooleanField(default=False) |
TINYINT(1) DEFAULT 0 |
DecimalField(max_digits=10, decimal_places=2) |
DECIMAL(10,2) |
ForeignKeyField("models.User", related_name="orders") |
INT/UUID + FK |
ManyToManyField("models.Tag", related_name="textbooks") |
自动建中间表 |
Meta 选项
class Meta:
table = "snake_case_name" # 显式表名
unique_together = (("a", "b"),) # 多字段唯一
indexes = (("a", "b"),) # 联合索引
ordering = ["-created_at"] # 默认排序
关系
# 1:N
class Slide(Model):
presentation = fields.ForeignKeyField(
"models.Presentation",
related_name="slides",
on_delete=fields.CASCADE,
)
# 反向访问
p = await Presentation.get(id=pid).prefetch_related("slides")
for s in p.slides: ...
# N:M
class Tag(Model):
textbooks = fields.ManyToManyField("models.Textbook", related_name="tags")
注册到 TORTOISE_ORM
TORTOISE_ORM = {
"connections": {"default": settings.database_url},
"apps": {
"models": {
"models": [
"models.session", "models.textbook", "models.referral",
# ...
"aerich.models", # 必须 — Aerich 自己用
],
"default_connection": "default",
},
},
}
查询模板
# 单条
brand = await Textbook.filter(id=tid).first()
# 批量(防 N+1)
items = await Textbook.filter(id__in=id_list).all()
item_map = {i.id: i for i in items}
# 条件 + 排序 + 分页
results = await Textbook.filter(subject="math").order_by("-created_at").offset(0).limit(20)
# 存在性检查
exists = await Textbook.filter(id=tid).exists()
# 创建
record = await Textbook.create(name="...", subject="math")
# 批量创建
records = [Textbook(name=n) for n in names]
await Textbook.bulk_create(records)
# 更新
await Textbook.filter(id=tid).update(name="new")
# 仅取部分字段(节省内存)
names = await Textbook.filter(...).values_list("name", flat=True)
自检
- [ ] 继承
TimestampMixin+Model? - [ ]
Meta.table显式 snake_case? - [ ] 索引字段加
index=True? - [ ] 外键明确
on_delete? - [ ] 新增 Model 后追加到
TORTOISE_ORM.models?