Tortoise ORM：今、みんなが使っている次世代のPython ORM


[![Group239.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4075813/579bd2ea-5e26-48e1-aec2-92c7dd16066a.png)](https://leapcell.io/)


**[Leapcell: 最適なサーバレス Web ホスティング](https://leapcell.io/)**

# Tortoise ORM: asyncio に基づく強力なオブジェクト関係マッパー
Tortoise ORM は、Django ORM にインスペクトされた、Python 向けの使いやすい asyncio ORM（オブジェクト関係マッパー）です。Django ORM のデザインコンセプトを借りています。従来のテーブルデータの処理をサポートするだけでなく、関係データを効率的に管理することもできます。パフォーマンス面では、他の Python ORM に劣りません。

## サポートされるデータベース
Tortoise ORM は現在、複数の主流のデータベースをサポートしています：
- **SQLite**：`aiosqlite` で駆動され、軽量なアプリケーションのシナリオに適しています。
- **PostgreSQL**：バージョンは >= 9.4 で、`asyncpg`（非同期モード）または `psycopg`（同期モード）ドライバをサポートしています。
- **MySQL/MariaDB**：`asyncmy` ドライバの助けを借りて、効率的な接続を実現します。
- **Microsoft SQL Server**：`asyncodbc` ドライバを通じてデータのやり取りを完了します。

## 環境設定とインストール
1. **Tortoise ORM のインストール**
```bash
pip install tortoise-orm
```
2. **対応するデータベースドライバのインストール**
    - **PostgreSQL（非同期）**:
    ```bash
    pip install tortoise-orm[asyncpg]
    ```
    - **MySQL/MariaDB**:
    ```bash
    pip install tortoise-orm[asyncmy]
    ```
    - **SQLite**: デフォルトでサポートされており、追加のドライバのインストールは不要です

## データベース接続設定
### SQLite
接続文字列の形式は `sqlite://DB_FILE` です。例えば、データベースファイルが `/data/DB.sqlite3` の場合、完全な接続文字列は `sqlite:///data/db.sqlite3`（スラッシュが 3 つ注意）です。

### MySQL
接続文字列の形式: `mysql://user:password@host:3306/somedb`、パラメータの説明:
- `user`: データベースのユーザ名
- `password`: ユーザパスワード
- `host`: データベースのホストアドレス
- `port`: データベースのポート（デフォルトは 3306）
- `database`: 接続するデータベースの名前

### PostgreSQL
- **非同期モード**: `asyncpg://postgres:pass@db.host:5432/somedb`
- **同期モード**: `psycopg://postgres:pass@db.host:5432/somedb`

### Microsoft SQL Server
接続文字列の形式: `mssql://user:pass@host:1433/db?driver=theodbcdriver`、パラメータの説明:
- `user`: ユーザ名
- `password`: パスワード
- `host`: ホストアドレス
- `port`: ポート（デフォルトは 1433）
- `database`: データベース名
- `driver`: ODBC ドライバ名で、`odbcinst.ini` ファイルで設定する必要があります

## データベースの作成と初期化
```python
from tortoise import Tortoise, run_async

async def init():
    # SQLite データベースを使用し、ファイル名は db.sqlite3
    # モデルを含むアプリケーション名を "models" と指定する
    await Tortoise.init(
        db_url='sqlite://db.sqlite3',
        modules={'models': ['models']}
    )
    # データベーステーブル構造を生成する
    await Tortoise.generate_schemas()  # safe パラメータ: True に設定すると、テーブルが存在しない場合のみ作成されます

run_async(init())  # 自動的にコンテキストを処理し、操作終了後にデータベース接続を閉じます
```
MySQL データベースを使用する場合、まず `tortoise-orm[aiomysql]` 依存関係をインストールする必要があります。

## モデル定義
```python
from tortoise.models import Model
from tortoise import fields
from tortoise.manager import Manager

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

    class Meta:
        abstract = False  # 抽象クラスかどうか、True の場合、データテーブルは生成されません
        table = "team"  # テーブル名、設定しない場合、クラス名がデフォルトで使用されます
        table_description = ""  # テーブルのコメント
        unique_together = ()  # 複合ユニークインデックス
        indexes = ()  # 複合非ユニークインデックス
        ordering = []  # デフォルトのソート
        manager = Manager  # カスタムマネージャ
```

## フィールドの種類
### データフィールド
```python
from tortoise import fields

fields.Field(
    source_field=None,  # カスタムデータベースカラム名
    generated=False,  # データベースで自動生成されるかどうか
    pk=False,  # 主キーかどうか
    null=False,  # フィールドが空にできるかどうか
    default=None,  # デフォルト値
    unique=False,  # 値が一意かどうか
    index=False,  # インデックスを作成するかどうか
    description=None,  # フィールドの説明
    validators=None  # バリデータのリスト
)
```

### 関係フィールド
1. **外部キーフィールド**
```python
fields.ForeignKeyField(
    model_name,  # 関連するモデル名、{app}.{models} の形式
    related_name=None,  # 逆解決属性名
    on_delete='CASCADE',  # 削除戦略、選択肢: CASCADE, RESTRICT, SET_NULL, SET_DEFAULT
    db_constraint=True,  # データベースで外部キー制約を作成するかどうか
)
```
2. **1 対 1 フィールド**
```python
fields.OneToOneField(
    model_name,
    related_name=None,
    on_delete='CASCADE',
    db_constraint=True
)
```
3. **多対多フィールド**
```python
fields.ManyToManyField(
    model_name,
    through=None,  # 中間テーブル
    forward_key=None,  # 前方検索キー
    backward_key='',  # 逆検索キー
    related_name='',
    on_delete='CASCADE',
    db_constraint=True
)
```

## 照会操作
モデルは複数の照会メソッドを提供しています：
- `filter(*args, **kwargs)`: 条件に応じてデータをフィルタリングします
- `exclude(*args, **kwargs)`: 条件を満たすデータを除外します
- `all()`: すべてのデータを取得します
- `first()`: 最初のデータを取得します
- `annotate()`: 集計照会を行います

`filter` メソッドがサポートする照会条件:
- **範囲照会**: `in`, `not_in`, `gte`, `gt`, `lte`, `lt`, `range`
- **null 値照会**: `isnull`, `not_isnull`
- **文字列照会**: `contains`, `icontains`, `startswith`, `istartswith`, `endswith`, `iendswith`, `iexact`
- **全文検索**: `search`

以下はいくつかの具体的な照会の例です：

### 単純な照会の例
`Team` モデルが定義されているとします：
```python
from tortoise import run_async
from models import Team  # モデルは models.py ファイルに定義されていると仮定

async def simple_query():
    # すべての Team レコードを取得する
    all_teams = await Team.all()
    print("All teams:", all_teams)

    # 最初の Team レコードを取得する
    first_team = await Team.first()
    print("The first team:", first_team)

    # 条件に基づいてフィルタリングし、名前が "Team A" のチームを取得する
    filtered_teams = await Team.filter(name="Team A")
    print("Teams named Team A:", filtered_teams)

run_async(simple_query())
```

### 範囲照会の例
```python
from tortoise import run_async
from models import Team

async def range_query():
    # id が 5 より大きいチームを照会する
    greater_than_5 = await Team.filter(id__gt=5)
    print("Teams with id greater than 5:", greater_than_5)

    # id が 2 から 8 の範囲（2 と 8 を含む）のチームを照会する
    in_range = await Team.filter(id__range=(2, 8))
    print("Teams with id between 2 and 8:", in_range)

    # id が [1, 3, 5] に含まれないチームを照会する
    not_in_list = await Team.filter(id__not_in=[1, 3, 5])
    print("Teams whose id is not in [1, 3, 5]:", not_in_list)

run_async(range_query())
```

### 文字列照会の例
```python
from tortoise import run_async
from models import Team

async def string_query():
    # 名前に "team" という文字列を含むチームを照会する（大文字小文字を区別しない）
    contains_team = await Team.filter(name__icontains="team")
    print("Teams whose name contains team (case-insensitive):", contains_team)

    # 名前が "A" で始まるチームを照会する（大文字小文字を区別する）
    startswith_A = await Team.filter(name__startswith="A")
    print("Teams whose name starts with A:", startswith_A)

    # 名前が "B" で終わるチームを照会する（大文字小文字を区別しない）
    endswith_B = await Team.filter(name__iendswith="B")
    print("Teams whose name ends with B (case-insensitive):", endswith_B)

run_async(string_query())
```
より詳細な使い方については、[Tortoise ORM 公式ドキュメント](https://tortoise.github.io/)を参照してください。

# [Leapcell: 最適なサーバレス Web ホスティング](https://leapcell.io/)

最後に、Python サービスをデプロイするのに最適なプラットフォームをお勧めします：**[Leapcell](https://leapcell.io/)**

[![brandpic7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4075813/415934bf-7963-49b1-a3e3-f0627f843d6e.png)](https://leapcell.io/)


### 🚀 好きな言語で構築
JavaScript、Python、Go、または Rust で簡単に開発できます。  

### 🌍 無料で無制限のプロジェクトをデプロイ
使用した分だけ支払います — リクエストがなければ、請求はありません。  

### ⚡ 使った分だけ支払い、隠された費用はありません
アイドル料金はなく、シームレスなスケーラビリティを実現します。  

[![Frame3-withpadding2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4075813/4a18c63c-923b-4b32-b321-d1128b7da89c.png)](https://leapcell.io/)


[📖 ドキュメントを参照](https://docs.leapcell.io/)  

🔹 Twitter でフォローしてください: [@LeapcellHQ](https://x.com/LeapcellHQ)