> For the complete documentation index, see [llms.txt](https://close.gitbook.io/yun-wei-bi-ji/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://close.gitbook.io/yun-wei-bi-ji/python/fastapi/fastapi-tortoiseorm-shi-yong-er.md).

# fastapi tortoise-orm 使用二

### 概述

> fastapi是一个很优秀的框架，但是缺少一个合适的orm，官方代码里面使用的是sqlalchemy，异步也是使用的这个。但是我这边看到有tortoise-orm这个异步orm框架，不知道效率如何，这里先学习，之后做一个性能测试比较一下。 整个框架非常接近django，如果我没写的地方，要么是和django差不多，要么是没这功能。

### fastapi引入

* 在main.py文件里面引入如下代码：

```python
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from models import User_Pydantic, UserIn_Pydantic, Users
from tortoise.contrib.fastapi import HTTPNotFoundError, register_tortoise

app = FastAPI(title="Tortoise ORM FastAPI example")

...
register_tortoise(#这里是启动app的，之后会考虑和使用uvicorn启动的性能差别
    app,
    db_url="sqlite://:memory:",#数据库信息
    modules={"models": ["models"]},#models列表
    generate_schemas=True,#如果数据库为空，则自动生成对应表单,生产环境不要开
    add_exception_handlers=True,#生产环境不要开，会泄露调试信息
)
```

* 引入tortoise-orm的方法：register\_tortoise 解释

```python
def register_tortoise(
    app: FastAPI,
    config: Optional[dict] = None,
    config_file: Optional[str] = None,
    db_url: Optional[str] = None,
    modules: Optional[Dict[str, List[str]]] = None,
    generate_schemas: bool = False,
    add_exception_handlers: bool = False,
) -> None:
    """
    在fastapi注册startup和shutdown

    使用 ``config``, ``config_file``或 ``(db_url, modules)``三者之一来配置
    示例
    ----------
    config:
        Dict containing config:
        Example
        -------
            {
                'connections': {
                    # Dict format for connection
                    'default': {
                        'engine': 'tortoise.backends.asyncpg',
                        'credentials': {
                            'host': 'localhost',
                            'port': '5432',
                            'user': 'tortoise',
                            'password': 'qwerty123',
                            'database': 'test',
                        }
                    },
                    # Using a DB_URL string
                    'default': 'postgres://postgres:qwerty123@localhost:5432/events'
                },
                'apps': {
                    'models': {
                        'models': ['__main__'],
                        # If no default_connection specified, defaults to 'default'
                        'default_connection': 'default',
                    }
                }
            }
    config_file:
        Path to .json or .yml (if PyYAML installed) file containing config with
        same format as above.
    db_url:
        Use a DB_URL string. See :ref:`db_url`
    modules:
        Dictionary of ``key``: [``list_of_modules``] that defined "apps" and modules that
        should be discovered for models.
    generate_schemas:
        True立即生成模式。仅适用于开发环境或SQLite ' ':memory: ' '数据库，生产环境数据库一定手动生成。
    add_exception_handlers:
        为' ' DoesNotExist ' ' & ' ' IntegrityError ' '添加一些自动异常处理程序。
不建议用于生产系统，因为它可能会泄漏数据
    """
    pass
```

### 创建对应数据模型

tortoise-orm能使用的数据类型还挺丰富，满足了日常使用的需求。

### 创建Model

```python
from tortoise import fields
from tortoise.models import Model
class User(Model):
	id=fields.IntField(pk=True)#主键必不可少
```

### 通过继承的方式创建Model

```python
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(pk=True)
    class Meta:
        abstract = True
#注意数据库里面对应表顺序可能会比较乱。。
class UserModel(TimestampMixin, MyAbstractBaseModel):
    # 覆盖继承的字段
    id = fields.UUIDField(pk=True)
    # 新增一些字段
    first_name = fields.CharField(20, null=True)
    class Meta:
        table = "user"

class RoleModel(TimestampMixin, NameMixin, MyAbstractBaseModel):
    class Meta:
        table = "role"
```

### 设置数据库字段field

* 主要有如下字段(这些字段都没啥好说的，):

```python

#常规字段
BigIntField,
BinaryField,
BooleanField,
CharEnumField,
CharField,
DateField,
DatetimeField,
DecimalField,
FloatField,
IntEnumField,#继承于SmallIntField
IntField,
JSONField,
SmallIntField,
TextField,
TimeDeltaField,
UUIDField,
#关系字段(relation不知道啥用，)
BackwardFKRelation,
BackwardOneToOneRelation,
ForeignKeyField,#外键
ForeignKeyNullableRelation,
ForeignKeyRelation,
ManyToManyField,#多对多
ManyToManyRelation,#反向代码提示的工具
OneToOneField,#一对一
OneToOneNullableRelation,
OneToOneRelation,
ReverseRelation,
```

关系键讲解： **ForeignKeyField**：

```python
tournament = fields.ForeignKeyField('models.Tournament', related_name='events')#related_name关键字参数，用于为引用的模型定义查询自己的字段，默认为名字+set
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)
```

反向代码提示： 在使用外键的时候，我们需要获取到反向代码提示（即被绑定的model查询绑定model），这个时候需要使用relation字段来提示，（其实你不加也没啥关系，只是个提示作用，在django里面是编辑器和插件提前内置了相关字段所以不用手写） 示例代码如下：

```python
from tortoise.models import Model
from tortoise import fields

class Tournament(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=255)
    events: fields.ReverseRelation["Event"]#仅用于代码提示，注意events必须和Event里面的外键指定的related_name同名

class Event(Model):
    id = fields.IntField(pk=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"
    )#注意多对多，两个model里面都要写，虽然复杂了点，但是有代码提示还是很合算的。。through在django里面是指定多对多表的名字和功能，需要手动创建，这里可能是示例代码不全吧。。得测试

class Team(Model):
    id = fields.IntField(pk=True)
    name = fields.CharField(max_length=255)
    events: fields.ManyToManyRelation[Event]#反向关系，
```

**字段介绍**

* 基本字段：
* ```
  source_field :自定义数据库对应字段名称
  ```
* ```
  generated ：是否映射到数据库，
  ```
* ```
  pk 是否为主键
  ```
* ```
  null
  ```
* ```
  default 可以为值或可调用对象
  ```
* ```
  unique
  ```
* ```
  index
  ```
* ```
  description 描述功能，数据库注释用
  ```

### 自定义字段

tortoise-orm支持自定义字段，这个功能挺好用的，这里先不讲了，一般用不到。。。请自行查看：link. 可以通过继承，然后重构相关字段控制字段在数据库的存储方式

```python
# 设置Meta
from tortoise import fields
from tortoise.models import Model
class User(Model):
	id = fields.IntField(pk=True)#主键必不可少
	class Meta:
		abstract=True#抽象模型，用于继承
		table="xxx"#该模型对应的表名称
		unique_together=(("field_a", "field_b"), )#设置唯一索引，参考django
		table_description = ""#数据库对该表的注释
		indexes=(("field_a", "field_b"), )#指定列集为非唯一索引，类似django在字段上的index
		ordering = ["name", "-score"]#设置默认查询结果的顺序
```

### Model模型方法

\#常用查询方法我就不提了，讲一些不常用的

```python
annotate()#使用额外的函数/聚合对结果进行再过滤，，
bulk_create()#批量插入：

User.bulk_create([
    User(name="...", email="..."),
    User(name="...", email="...")
])

check()#检查model数据是否正确
describe()#序列化model，返回json
exists()#True/False 记录是否存在筛选器参数模式的model
register_listener()#侦听器，参数为信号，注意，更新到最新版才有。。数据的保存和删除前后可以被监听，挺有用的一个东西，
update_from_dict()#通过dict更新数据，配合schema很有用，主要用于字段更新，schema.dict有一个只获取填写结果字段的方法，配合这个可以实现局部更新
```

### 查询

参考django，略。

### Q对象查询

这一块主要针对复杂查询。 有时，您需要执行比简单 AND 提供的更复杂的查询。幸运的是，我们有Q对象来调味的东西，并帮助您找到你需要的。然后，这些 Q 对象可用作参数。.filter().filter()

**Q 对象用途极多，例如用例：**

* 创建 OR 筛选器
* 嵌套筛选器
* 倒置过滤器 例如，查找名称或 的事件：Event 1Event 2

```python
found_events = await Event.filter(
    Q(name='Event 1') | Q(name='Event 2')
)
#等效于：
	found_events = await Event.filter(
    Q(Q(name='Event 1'), Q(name='Event 2'), join_type="OR")#如果省略join_type，则为AND
)
```

### 字段过滤

在搜索的时候增加后缀可以实现字段的过滤效果： 比如：

```python
teams = await Team.filter(name__icontains='CON')
```

相关参数如下：

* not
* in- 检查字段的值是否位于传递列表中
* not\_in
* gte- 大于或等于传递的值
* gt- 大于传递的值
* lte- 低于或等于传递的值
* lt- 低于传递值
* range- 介于和给定两个值之间
* isnull- 字段为空
* not\_isnull- 字段不为空
* contains- 字段包含指定的子字符串
* icontains- 不区分大小写contains
* startswith- 如果字段以值开头
* istartswith- 不区分大小写startswith
* endswith- 如果字段以值结尾
* iendswith- 不区分大小写endswith
* iequals- 区分大小写等于

### 预取

通过预取，可以减少数据库读取次数，然后提高响应速度 有时只需要获取某些相关记录。您可以使用对象实现：Prefetch 示例：

```python
tournament_with_filtered = await Tournament.all().prefetch_related(
    Prefetch('events', queryset=Event.filter(name='First'))
).first()
```

### F表达式

某些时候，我们只是需要将数据进行一次计算或处理然后保存，我们并不在意值是多少，只是想把值进行我们指定的修改，就可以使用F表达式，这样就可以减少一次数据库读取（我感觉好像没卵用啊。。。) 参考如下：

```python
from tortoise.expressions import F
await User.filter(id=1).update(balance = F('balance') - 10)
await User.filter(id=1).update(balance = F('balance') + F('award'), award = 0)
```

### or use .save()

```python
user = await User.get(id=1)
user.balance = F('balance') - 10
await user.save(update_fields=['balance'])
```

### 功能和聚合

略 请参考功能和聚合

### 事务

略 请参考事务

### 根据Model生成Schema

讲道理schema这个东西名字挺奇葩的。。。不过既然官网这么弄就这么弄吧。这个可以很方便的生成相关字段

> 注意，schema不要有相同的类名，会报错的

```python
User_Pydantic = pydantic_model_creator(Users, name="User")
UserIn_Pydantic = pydantic_model_creator(Users, name="UserIn", exclude_readonly=True)
```

下面这个文档里面还没讲解; 可以通过在model里面创建一个class PydanticMeta来实现创建schema的控制：

```python
class PydanticMeta:
    """
    The ``PydanticMeta`` class is used to configure metadata for generating the pydantic Model.

    Usage:

    .. code-block:: python3

        class Foo(Model):
            ...

            class PydanticMeta:
                exclude = ("foo", "baa")
                computed = ("count_peanuts", )
    """

    #: If not empty, only fields this property contains will be in the pydantic model
    include: Tuple[str, ...] = ()

    #: Fields listed in this property will be excluded from pydantic model
    exclude: Tuple[str, ...] = ()

    #: Computed fields can be listed here to use in pydantic model
    computed: Tuple[str, ...] = ()

    #: Use backward relations without annotations - not recommended, it can be huge data
    #: without control
    backward_relations: bool = True

    #: Maximum recursion level allowed
    max_recursion: int = 3

    #: Allow cycles in recursion - This can result in HUGE data - Be careful!
    #: Please use this with ``exclude``/``include`` and sane ``max_recursion``
    allow_cycles: bool = False

    #: If we should exclude raw fields (the ones have _id suffixes) of relations
    exclude_raw_fields: bool = True

    #: Sort fields alphabetically.
    #: If not set (or ``False``) then leave fields in declaration order
    sort_alphabetically: bool = False

```

> 如果你想跨表搜索或join搜索，在computed里面定义。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://close.gitbook.io/yun-wei-bi-ji/python/fastapi/fastapi-tortoiseorm-shi-yong-er.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
