模型

用法

要开始使用模型,首先应该导入它们

from tortoise.models import Model

有了它,你可以像这样开始描述你自己的模型

class Tournament(Model):
    id = fields.IntField(primary_key=True)
    name = fields.TextField()
    created = fields.DatetimeField(auto_now_add=True)

    def __str__(self):
        return self.name


class Event(Model):
    id = fields.IntField(primary_key=True)
    name = fields.TextField()
    tournament = fields.ForeignKeyField('models.Tournament', related_name='events')
    participants = fields.ManyToManyField('models.Team', related_name='events', through='event_team')
    modified = fields.DatetimeField(auto_now=True)
    prize = fields.DecimalField(max_digits=10, decimal_places=2, null=True)

    def __str__(self):
        return self.name


class Team(Model):
    id = fields.IntField(primary_key=True)
    name = fields.TextField()

    def __str__(self):
        return self.name

让我们详细看看我们在这里完成了什么

class Tournament(Model):

每个模型都应该派生自基本模型。你也可以从你自己的模型子类派生,并且可以像这样创建抽象模型

class AbstractTournament(Model):
    id = fields.IntField(primary_key=True)
    name = fields.TextField()
    created = fields.DatetimeField(auto_now_add=True)

    class Meta:
        abstract = True

    def __str__(self):
        return self.name

这些模型不会在架构生成中创建,也不会与其他模型创建关系。

此外,我们有字段 fields.DatetimeField(auto_now=True)。选项 auto_nowauto_now_add 的工作方式与 Django 的选项类似。

__models__ 的用法

如果你在加载模型的模块中定义变量 __models__generate_schema 将使用该列表,而不是自动为你查找模型。

主键

在 Tortoise ORM 中,我们要求模型具有主键。

该主键可以通过保留字段 pk 访问,该字段将是已被指定为主键的任何字段的别名。该别名字段可在执行过滤时用作字段名称,例如 .filter(pk=...) 等…

注意

我们目前支持任何可索引字段类型的单个(非复合)主键,但仅建议使用这些字段类型

IntField
BigIntField
CharField
UUIDField

必须通过将 primary_key 参数设置为 True 来定义主键。如果你未定义主键,我们将为你创建一个名称为 idIntField 类型的 primary_key

注意

如果将其用于 Integer Field,则 generated 将被设置为 True,除非你明确传递 generated=False

以下任何一种都是模型中有效的主键定义

id = fields.IntField(primary_key=True)

checksum = fields.CharField(primary_key=True)

guid = fields.UUIDField(primary_key=True)

继承

在 Tortoise ORM 中定义模型时,你可以通过利用继承来节省大量重复工作。

您可以在更通用的类中定义字段,它们在派生类中自动可用。基类不限于 Model 类。任何类都可以使用。通过这种方式,您能够以自然且易于维护的方式定义模型。

让我们看一些示例。

from tortoise import fields
from tortoise.models import Model

class TimestampMixin():
    created_at = fields.DatetimeField(null=True, auto_now_add=True)
    modified_at = fields.DatetimeField(null=True, auto_now=True)

class NameMixin():
    name = fields.CharField(40, unique=True)

class MyAbstractBaseModel(Model):
    id = fields.IntField(primary_key=True)

    class Meta:
        abstract = True

class UserModel(TimestampMixin, MyAbstractBaseModel):
    # Overriding the id definition
    # from MyAbstractBaseModel
    id = fields.UUIDField(primary_key=True)

    # Adding additional fields
    first_name = fields.CharField(20, null=True)

    class Meta:
        table = "user"


class RoleModel(TimestampMixin, NameMixin, MyAbstractBaseModel):

    class Meta:
        table = "role"

使用 Meta 类不是必需的。但养成一个好习惯,为您的表指定一个明确的名称。这样,您就可以在不破坏架构的情况下更改模型名称。因此,以下定义是有效的。

class RoleModel(TimestampMixin, NameMixin, MyAbstractBaseModel):
    pass

Meta

class tortoise.models.Model.Meta

Meta 类用于配置模型的元数据。

用法

class Foo(Model):
    ...

    class Meta:
        table="custom_table"
        unique_together=(("field_a", "field_b"), )
abstract = False

设置为 True 以指示这是一个抽象类

schema = ""

将此设置为配置架构名称,其中存在表

table = ""

将此设置为配置手动表名称,而不是生成表名称

table_description = ""

将此设置为生成当前模型所创建表的注释消息

unique_together = None

指定 unique_together 以为列集设置复合唯一索引。

它应该是格式为的元组元组(列表也可以)

unique_together=("field_a", "field_b")
unique_together=(("field_a", "field_b"), )
unique_together=(("field_a", "field_b"), ("field_c", "field_d", "field_e"))
indexes = None

指定 indexes 以为列集设置复合非唯一索引。

它应该是格式为的元组元组(列表也可以)

indexes=("field_a", "field_b")
indexes=(("field_a", "field_b"), )
indexes=(("field_a", "field_b"), ("field_c", "field_d", "field_e"))
ordering = None

指定 ordering 以为给定的模型设置默认排序。它应该是以与 .order_by(...) 接收相同方式格式化的字符串的可迭代对象。如果使用 GROUP_BY 子句使用 .annotate(...) 构建查询,则不应用默认排序。

ordering = ["name", "-score"]
manager = tortoise.manager.Manager

指定 manager 以覆盖默认管理器。它应该是 tortoise.manager.Manager 或其子类的实例。

manager = CustomManager()

ForeignKeyField

tournament = fields.ForeignKeyField('models.Tournament', related_name='events')
participants = fields.ManyToManyField('models.Team', related_name='events')
modified = fields.DatetimeField(auto_now=True)
prize = fields.DecimalField(max_digits=10, decimal_places=2, null=True)

在事件模型中,我们获得了一些对我们来说可能很有趣的字段。

fields.ForeignKeyField('models.Tournament', related_name='events')

此处我们创建对锦标赛的外键引用。我们通过引用模型及其文字(由应用程序名称和模型名称组成)来创建它。 models 是默认应用程序名称,但您可以在 class Meta 中使用 app = 'other' 对其进行更改。

related_name

是关键字参数,它定义了引用模型上相关查询的字段,因此您可以使用它以这种方式获取所有锦标赛的事件

await Tournament.first().prefetch_related("events")

数据库支持字段

注意

ForeignKeyField 是一个虚拟字段,这意味着它没有直接的数据库支持。相反,它有一个字段(默认称为 FKNAME_id(即,仅追加 _id)),这是实际的数据库支持字段。

它将仅包含相关表的键值。

这是一个重要的细节,因为它允许直接分配/读取实际值,如果不需要整个外来对象,则可以将其视为优化。

可以通过传递对象来指定 FK

await SomeModel.create(tournament=the_tournament)
# or
somemodel.tournament=the_tournament

或通过直接访问数据库支持字段

await SomeModel.create(tournament_id=the_tournament.pk)
# or
somemodel.tournament_id=the_tournament.pk

查询关系通常是通过追加双下划线,然后追加外来对象的字段来完成的。然后可以追加一个普通查询属性。如果下一个键也是一个外来对象,则可以对其进行链接

FKNAME__FOREIGNFIELD__gt=3

FKNAME__FOREIGNFK__VERYFOREIGNFIELD__gt=3

然而,有一个主要的限制。我们不想限制外来列名,或产生歧义(例如,外来对象可能有一个名为 isnull 的字段)

那么这将完全模棱两可

FKNAME__isnull

为了防止这种情况,我们要求将直接过滤器应用于外键的数据库支持字段

FKNAME_id__isnull

获取外来对象

可以使用异步和同步接口获取外键。

异步获取

events = await tournament.events.all()

您可以像这样异步迭代它

async for event in tournament.events:
    ...

同步使用要求您在时间之前调用 fetch_related,然后您可以使用诸如

await tournament.fetch_related('events')
events = list(tournament.events)
eventlen = len(tournament.events)
if SomeEvent in tournament.events:
    ...
if tournament.events:
    ...
firstevent = tournament.events[0]

要获取反向 FK,例如 event.tournament,我们目前仅支持同步接口。

await event.fetch_related('tournament')
tournament = event.tournament

ManyToManyField

下一个字段是 fields.ManyToManyField('models.Team', related_name='events')。它描述了与模型 Team 的多对多关系。

要添加到 ManyToManyField,两个模型都需要保存,否则你会收到一个 OperationalError 异常。

可以通过异步和同步接口来解析多对多字段。

异步获取

participants = await tournament.participants.all()

您可以像这样异步迭代它

async for participant in tournament.participants:
    ...

同步使用要求您在时间之前调用 fetch_related,然后您可以使用诸如

await tournament.fetch_related('participants')
participants = list(tournament.participants)
participantlen = len(tournament.participants)
if SomeParticipant in tournament.participants:
    ...
if tournament.participants:
    ...
firstparticipant = tournament.participants[0]

team.event_team 的反向查找工作方式完全相同。

改进关系类型提示

由于 Tortoise ORM 仍然是一个年轻的项目,它没有得到各种编辑器的广泛支持,这些编辑器帮助你使用模型和它们之间的不同关系编写代码,并提供良好的自动完成。但是,你可以通过自己做一些工作来获得这种自动完成。你需要做的就是为负责关系的字段向你的模型添加一些注释。

这是一个来自 入门 的更新示例,它将为所有模型添加自动完成,包括模型之间关系的字段。

from tortoise.models import Model
from tortoise import fields


class Tournament(Model):
    id = fields.IntField(primary_key=True)
    name = fields.CharField(max_length=255)

    events: fields.ReverseRelation["Event"]

    def __str__(self):
        return self.name


class Event(Model):
    id = fields.IntField(primary_key=True)
    name = fields.CharField(max_length=255)
    tournament: fields.ForeignKeyRelation[Tournament] = fields.ForeignKeyField(
        "models.Tournament", related_name="events"
    )
    participants: fields.ManyToManyRelation["Team"] = fields.ManyToManyField(
        "models.Team", related_name="events", through="event_team"
    )

    def __str__(self):
        return self.name


class Team(Model):
    id = fields.IntField(primary_key=True)
    name = fields.CharField(max_length=255)

    events: fields.ManyToManyRelation[Event]

    def __str__(self):
        return self.name

参考

class tortoise.models.Model(**kwargs)[source]

所有 Tortoise ORM 模型的基本类。

class Meta[source]

Meta 类用于配置模型的元数据。

用法

class Foo(Model):
    ...

    class Meta:
        table="custom_table"
        unique_together=(("field_a", "field_b"), )
classmethod all(using_db=None)[source]

返回完整的 QuerySet。

返回类型:

QuerySet[typing_extensions.Self]

类方法 annotate(**kwargs)[源代码]

使用额外的函数/聚合/表达式对结果集进行注释。

参数:
**kwargs

参数名称和用于注释的函数/聚合。

返回类型:

QuerySet[typing_extensions.Self]

类方法 bulk_create(objects, batch_size=None, ignore_conflicts=False, update_fields=None, on_conflict=None, using_db=None)[源代码]

批量插入操作

注意

批量插入操作将执行最少操作以确保在数据库中创建的对象具有所有默认值和已设置的生成字段,但在 Python 中可能是不完整的引用。

例如 IntField 主键将不会填充。

仅建议在您希望确保最佳插入性能的一次性插入中使用此方法。

User.bulk_create([
    User(name="...", email="..."),
    User(name="...", email="...")
])
参数:
on_conflict=None

冲突索引名称

update_fields=None

冲突时更新字段

ignore_conflicts=False

插入时忽略冲突

objects

要批量创建的对象列表

batch_size=None

单个查询中创建的对象数量

using_db=None

特定 DB 连接,用于替代默认绑定

返回类型:

BulkCreateQuery[Model]

classmethod bulk_update(objects, fields, batch_size=None, using_db=None)[源代码]

更新数据库中每个给定对象中给定的字段。此方法有效地更新已提供模型实例上的给定字段,通常使用一个查询。

users = [
    await User.create(name="...", email="..."),
    await User.create(name="...", email="...")
]
users[0].name = 'name1'
users[1].name = 'name2'

await User.bulk_update(users, fields=['name'])
参数:
objects

要批量创建的对象列表

fields

要更新的字段

batch_size=None

单个查询中创建的对象数量

using_db=None

特定 DB 连接,用于替代默认绑定

返回类型:

BulkUpdateQuery[Model]

clone(pk=<object object>)[source]

创建对象的克隆,当执行 .save() 时,将创建一个新记录。

参数:
pk : Any

如果模型不生成自己的主键,则为可选必需值。此处指定的任何值都将始终使用。

返回类型:

Model

返回:

不含主键信息的当前对象的副本。

引发:

ParamsError – 如果需要 pk 但未提供。

async classmethod create(using_db=None, **kwargs)[source]

在 DB 中创建记录并返回对象。

user = await User.create(name="...", email="...")

等同于

user = User(name="...", email="...")
await user.save()
参数:
using_db=None

特定 DB 连接,用于替代默认绑定

**kwargs

模型参数。

返回类型:

Model

async delete(using_db=None)[source]

删除当前模型对象。

参数:
using_db=None

特定 DB 连接,用于替代默认绑定

引发:

OperationalError – 如果对象从未持久化过。

返回类型:

classmethod describe(serializable=True)[source]

描述给定的模型列表或所有已注册的模型。

参数:
serializable=True

False 如果想要原始 Python 对象,True 如果想要 JSON 可序列化数据。(默认为 True

返回类型:

字典

返回:

包含模型描述的字典。

基本字典有一组固定的键,它们引用字段列表(或在主键的情况下引用单个字段)

{
    "name":                 str     # Qualified model name
    "app":                  str     # 'App' namespace
    "table":                str     # DB table name
    "abstract":             bool    # Is the model Abstract?
    "description":          str     # Description of table (nullable)
    "docstring":            str     # Model docstring (nullable)
    "unique_together":      [...]   # List of List containing field names that
                                    #  are unique together
    "pk_field":             {...}   # Primary key field
    "data_fields":          [...]   # Data fields
    "fk_fields":            [...]   # Foreign Key fields FROM this model
    "backward_fk_fields":   [...]   # Foreign Key fields TO this model
    "o2o_fields":           [...]   # OneToOne fields FROM this model
    "backward_o2o_fields":  [...]   # OneToOne fields TO this model
    "m2m_fields":           [...]   # Many-to-Many fields
}

每个字段都按 tortoise.fields.base.Field.describe() 中定义的方式指定

类方法 排除(*args, **kwargs)[源代码]

生成应用了排除的 QuerySet。

参数:
*args

包含约束的 Q 函数。将进行 AND 操作。

**kwargs

简单的筛选约束。

返回类型:

QuerySet[typing_extensions.Self]

类方法 存在(*args, using_db=None, **kwargs)[源代码]

返回 True/False,表示是否使用提供的筛选参数存在记录。

result = await User.exists(username="foo")
参数:
using_db=None

要使用的特定 DB 连接。

*args

包含约束的 Q 函数。将进行 AND 操作。

**kwargs

简单的筛选约束。

返回类型:

ExistsQuery

async classmethod fetch_for_list(instance_list, *args, using_db=None)[source]

获取指定 Model 对象列表的相关模型。

参数:
instance_list

要获取关系的 Model 对象列表。

*args

要获取的关系名称。

using_db=None

不要使用

返回类型:

获取相关字段。

User.fetch_related("emails", "manager")
*args

应获取的相关字段。

using_db=None

特定 DB 连接,用于替代默认绑定

classmethod filter(*args, **kwargs)[source]

生成应用了筛选器的 QuerySet。

参数:
*args

包含约束的 Q 函数。将进行 AND 操作。

**kwargs

简单的筛选约束。

返回类型:

QuerySet[typing_extensions.Self]

classmethod first(using_db=None)[source]

生成返回第一条记录的 QuerySet。

返回类型:

QuerySetSingle[Optional[typing_extensions.Self]]

classmethod get(*args, using_db=None, **kwargs)[source]

使用提供的筛选器参数获取 Model 类型的一个记录。

user = await User.get(username="foo")
参数:
using_db=None

要使用的数据库连接

*args

包含约束的 Q 函数。将进行 AND 操作。

**kwargs

简单的筛选约束。

引发:
返回类型:

QuerySetSingle[typing_extensions.Self]

async classmethod get_or_create(defaults=None, using_db=None, **kwargs)[源代码]

如果存在则获取对象(根据提供的参数进行过滤),否则创建一个实例,将任何未指定的参数作为默认值。

参数:
defaults=None

如果无法获取创建的实例,则要添加到该实例的默认值。

using_db=None

特定 DB 连接,用于替代默认绑定

**kwargs

查询参数。

引发:
返回类型:

Tuple[typing_extensions.Self, bool]

classmethod get_or_none(*args, using_db=None, **kwargs)[source]

使用提供的筛选参数获取 Model 类型的单个记录,或获取 None。

user = await User.get_or_none(username="foo")
参数:
using_db=None

要使用的特定 DB 连接。

*args

包含约束的 Q 函数。将进行 AND 操作。

**kwargs

简单的筛选约束。

返回类型:

QuerySetSingle[Optional[typing_extensions.Self]]

async classmethod in_bulk(id_list, field_name='pk', using_db=None)[source]

返回一个字典,将每个给定的 ID 映射到具有该 ID 的对象。如果未提供 id_list,则计算整个 QuerySet。

参数:
id_list

字段值列表

field_name='pk'

必须是唯一字段

using_db=None

特定 DB 连接,用于替代默认绑定

返回类型:

Dict[str, Model]

property pk : Any

模型主键别名。可在执行过滤时用作字段名,例如 .filter(pk=...) 等…

返回类型:

Any

classmethod raw(sql, using_db=None)[source]

执行 RAW SQL 并返回结果

result = await User.raw("select * from users where name like '%test%'")
参数:
using_db=None

要使用的特定数据库连接

sql

原始 SQL。

返回类型:

RawSQLQuery

async refresh_from_db(fields=None, using_db=None)[source]

从数据库刷新最新数据。当不带参数调用此方法时,模型的所有数据库字段都会更新为当前数据库中存在的的值。

user.refresh_from_db(fields=['name'])
参数:
fields=None

要刷新的特殊字段。

using_db=None

要使用的特定数据库连接,而不是默认绑定的。

引发:

OperationalError – 如果对象从未持久化过。

返回类型:

classmethod register_listener(signal, listener)[source]

为当前模型类注册侦听器以获取特殊信号。

参数:
signal

tortoise.signals.Signals 之一

listener

可调用的监听器

引发:

ConfigurationError – 当监听器不可调用时

async save(using_db=None, update_fields=None, force_create=False, force_update=False)[source]

创建/更新当前模型对象。

参数:
update_fields=None

如果提供,它应为按名称排列的字段的元组/列表。

这是应更新的字段子集。如果需要创建对象,则将忽略 update_fields

using_db=None

特定 DB 连接,用于替代默认绑定

force_create=False

强制创建记录

force_update=False

强制更新记录

引发:
  • IncompleteInstanceError – 如果模型是部分的,并且字段不可用于持久性。

  • IntegrityError – 如果模型无法创建或更新(特别是如果已设置 force_create 或 force_update)

返回类型:

classmethod select_for_update(nowait=False, skip_locked=False, of=(), using_db=None)[source]

使 QuerySet 选择更新。

返回一个查询集,它将锁定行直到事务结束,在支持的数据库上生成一个 SELECT … FOR UPDATE SQL 语句。

返回类型:

QuerySet[typing_extensions.Self]

update_from_dict(data)[source]

使用提供的字典更新当前模型。这允许从字典中批量更新模型,同时确保进行数据类型转换。

这将忽略任何额外的字段,并且不会用它们更新模型,但会针对错误的类型或更新多实例关系引发错误。

参数:
data

您想要以字典格式更新的参数

返回类型:

Model

返回:

当前模型实例

引发:
  • ConfigurationError – 当试图更新远程实例时(例如反向 ForeignKey 或 ManyToMany 关系)

  • ValueError – 当传递的参数类型不兼容时

async classmethod update_or_create(defaults=None, using_db=None, **kwargs)[源代码]

一种便捷的方法,可使用给定的 kwargs 更新对象,必要时创建新对象。

参数:
defaults=None

用于更新对象的默认值。

using_db=None

特定 DB 连接,用于替代默认绑定

**kwargs

查询参数。

返回类型:

Tuple[Model, bool]