模型¶
用法¶
要开始使用模型,首先应该导入它们
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_now
和 auto_now_add
的工作方式与 Django 的选项类似。
__models__
的用法¶
如果你在加载模型的模块中定义变量 __models__
,generate_schema
将使用该列表,而不是自动为你查找模型。
主键¶
在 Tortoise ORM 中,我们要求模型具有主键。
该主键可以通过保留字段 pk
访问,该字段将是已被指定为主键的任何字段的别名。该别名字段可在执行过滤时用作字段名称,例如 .filter(pk=...)
等…
注意
我们目前支持任何可索引字段类型的单个(非复合)主键,但仅建议使用这些字段类型
IntField
BigIntField
CharField
UUIDField
必须通过将 primary_key
参数设置为 True
来定义主键。如果你未定义主键,我们将为你创建一个名称为 id
的 IntField
类型的 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"), )
-
类方法 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="...") ])
-
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'])
-
async classmethod create(using_db=
None
, **kwargs)[source]¶ 在 DB 中创建记录并返回对象。
user = await User.create(name="...", email="...")
等同于
user = User(name="...", email="...") await user.save()
-
async delete(using_db=
None
)[source]¶ 删除当前模型对象。
- 参数:¶
- using_db=
None
¶ 特定 DB 连接,用于替代默认绑定
- using_db=
- 引发:¶
OperationalError – 如果对象从未持久化过。
- 返回类型:¶
无
-
classmethod describe(serializable=
True
)[source]¶ 描述给定的模型列表或所有已注册的模型。
- 参数:¶
- serializable=
True
¶ False
如果想要原始 Python 对象,True
如果想要 JSON 可序列化数据。(默认为True
)
- serializable=
- 返回类型:¶
字典
- 返回:¶
包含模型描述的字典。
基本字典有一组固定的键,它们引用字段列表(或在主键的情况下引用单个字段)
{ "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, using_db=
None
, **kwargs)[源代码]¶ 返回 True/False,表示是否使用提供的筛选参数存在记录。
result = await User.exists(username="foo")
-
async classmethod fetch_for_list(instance_list, *args, using_db=
None
)[source]¶ 获取指定 Model 对象列表的相关模型。
获取相关字段。
User.fetch_related("emails", "manager")
应获取的相关字段。
特定 DB 连接,用于替代默认绑定
无
-
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")
- 参数:¶
- 引发:¶
MultipleObjectsReturned – 如果提供的搜索返回多个对象。
DoesNotExist – 如果找不到对象。
- 返回类型:¶
QuerySetSingle
[typing_extensions.Self]
-
async classmethod get_or_create(defaults=
None
, using_db=None
, **kwargs)[源代码]¶ 如果存在则获取对象(根据提供的参数进行过滤),否则创建一个实例,将任何未指定的参数作为默认值。
- 参数:¶
- 引发:¶
IntegrityError – 如果创建失败
TransactionManagementError – 如果发生事务错误
ParamsError – 如果默认值与关键字参数冲突
- 返回类型:¶
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")
-
async classmethod in_bulk(id_list, field_name=
'pk'
, using_db=None
)[source]¶ 返回一个字典,将每个给定的 ID 映射到具有该 ID 的对象。如果未提供 id_list,则计算整个 QuerySet。
-
classmethod raw(sql, using_db=
None
)[source]¶ 执行 RAW SQL 并返回结果
result = await User.raw("select * from users where name like '%test%'")
-
async refresh_from_db(fields=
None
, using_db=None
)[source]¶ 从数据库刷新最新数据。当不带参数调用此方法时,模型的所有数据库字段都会更新为当前数据库中存在的的值。
user.refresh_from_db(fields=['name'])
- classmethod register_listener(signal, listener)[source]¶
为当前模型类注册侦听器以获取特殊信号。
- 参数:¶
- 引发:¶
ConfigurationError – 当监听器不可调用时
-
async save(using_db=
None
, update_fields=None
, force_create=False
, force_update=False
)[source]¶ 创建/更新当前模型对象。
- 参数:¶
- 引发:¶
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 语句。