SQLModel × FastAPIのチュートリアルのメモ②

今回はFastAPIとSQLModelを用いて簡単なヒーローのデータベースとAPIを作成するチュートリアルの和訳です。

https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/

引用元は上記。
機械翻訳をガッツリ使って、一部意訳しているので間違いがあるかもしれません。


# Simple Hero API with FastAPI[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#simple-hero-api-with-fastapi "Permanent link")

まずは、**FastAPI**を使って、シンプルなヒーローWeb APIを作ってみましょう。✨

## Install **FastAPI**[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#install-fastapi "Permanent link")

まず最初にFastAPIをインストールします。

FastAPIは**web API**を作るためのフレームワークです。
しかし、WebAPIの実行にはサーバーと呼ばれる別のアプリも必要です。FastAPIでは**Uvicorn** を使います。Uvicornを標準的な依存関係とともにインストールしていきます。
[仮想環境が有効になっている](https://sqlmodel.tiangolo.com/tutorial/#create-a-python-virtual-environment)ことを確認してください。
確認できたら、FastAPIとUvicornをインストールします。

```terminal
$ python -m pip install fastapi "uvicorn[standard]"
```

## **SQLModel** Code - Models, Engine[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#sqlmodel-code-models-engine "Permanent link")

それでは、SQLModelのコードを見ていきましょう。
ここでは、ヒーローだけの **最もシンプルなバージョン** から始めます（まだチームはありません）。
他のSQLModelのチュートリアルで見てきたものとほとんど同じコードです。

```py
from typing import Optional

# One line of FastAPI imports here later 👈
from sqlmodel import Field, Session, SQLModel, create_engine, select

class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None

sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"

connect_args = {"check_same_thread": False}
engine = create_engine(sqlite_url, echo=True, connect_args=connect_args)


def create_db_and_tables():
    SQLModel.metadata.create_all(engine)

# Code below omitted 👇
```


ここで以前使っていたコードからの変更点は1つだけで、`connect_args`の`check_same_thread`です。これは、SQLAlchemyがデータベースとの通信を担当する低レベルライブラリに渡す設定です。`check_same_thread` はデフォルトでは `True` に設定されていますが、これは単純なケースで誤用することを防ぐためです。しかし、ここでは複数のリクエストで同じ**session**を共有しないようにします。これが、この設定が必要とする問題を防ぐための、実際の**最も安全な方法**です。
また、**FastAPI**では各リクエストが複数のインタラクティブなスレッドで処理される可能性があるため、これを無効にする必要があります。

>#### 情報
詳細は[FastAPI docs for `async` and `await`](https://fastapi.tiangolo.com/async/)を参照してください。
重要なのは、複数のリクエストで同じ **セッション** を共有しないようにすることで、安全にコードを扱えます。

## **FastAPI** App[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#fastapi-app "Permanent link")

次のステップでは、**FastAPI**アプリを作成します。
まず、`fastapi`から`FastAPI`クラスをインポートします。
そして、その`FastAPI`クラスのインスタンスである`app`オブジェクトを作成します。

```py
from typing import Optional
from fastapi import FastAPI
from sqlmodel import Field, Session, SQLModel, create_engine, select

# SQLModel code here omitted 👈

app = FastAPI()

# Code below omitted 👇

```

## Create Database and Tables on `startup`[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#create-database-and-tables-on-startup "Permanent link")

アプリの実行が始まったら、関数`create_tables`が呼ばれるようにしたいと思います。データベースとテーブルを作成するためです。
これは起動時に一度だけ呼ばれるべきもので、毎回のリクエストの前に呼ばれるものではありませんので、`startup`イベントを処理する関数の中に入れています。

```py
# 上のコードは省略しています👆。
app = FastAPI()
@app.on_event("startup") def on_startup():
 create_db_and_tables() 
# 以下のコードは省略👇
```

## Create Heroes _Path Operation_[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#create-heroes-path-operation "Permanent link")

>#### infomation
**Path Operation**とは何か（特定のHTTP Operationを持つエンドポイント）、そしてFastAPIでどのように動作するかについて再確認する必要がある場合は、[FastAPI First Steps docs](https://fastapi.tiangolo.com/tutorial/first-steps/)をご覧ください。


新しいヒーローを作成するための **Path Operation** コードを作成してみましょう。

これは、ユーザーが `/heroes/` **path** に `POST` **operation** でリクエストを送ったときに呼び出されます。

```py
# Code above omitted 👆

app = FastAPI()


@app.on_event("startup")
def on_startup():
    create_db_and_tables()



@app.post("/heroes/")

def create_hero(hero: Hero):

    with Session(engine) as session:
        session.add(hero)
        session.commit()
        session.refresh(hero)
        return hero

# Code below omitted 👇

```



>情報
これらのコンセプトについて再確認する必要がある場合は、FastAPIのドキュメントをチェックしてください。

- [ファーストステップ](https://fastapi.tiangolo.com/tutorial/first-steps/)
- パスパラメータ - データ検証とデータ変換](https://fastapi.tiangolo.com/tutorial/path-params/)
- [リクエストボディ](https://fastapi.tiangolo.com/tutorial/body/)

## The **SQLModel** Advantage[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#the-sqlmodel-advantage "Permanent link")

ここで、**SQLModel**クラスのモデルが、**SQLAlchemy**モデルと**Pydantic**モデルの両方を同時に持っていることが光ります。✨

ここでは、同じ**クラスモデルを使って、APIが受け取る**リクエストボディ**を定義しています。

FastAPI**はPydanticをベースにしているので、同じモデル（Pydanticの部分）を使って自動的にデータを検証し、JSONリクエストから実際の`Hero`クラスのインスタンスであるオブジェクトに変換します。

そして、この同じ**SQLModel**オブジェクトは**Pydantic**モデルのインスタンスであるだけでなく、**SQLAlchemy**モデルのインスタンスでもあるので、データベースに行を作成するために**session**で直接使用することができます。

直感的にPythonの標準的な**typeアノテーション**を使用することができ、データベースモデルとAPIデータモデルのための多くのコードを重複させる必要はありません。🎉

>#### ヒント
このコードは後でさらに改良する予定ですが、今のところ、**SQLModel**クラスが**SQLAlchemy**モデルと**Pydantic**モデルの両方を同時に持つことの威力をすでに示しています。

## heroを読む _Path Operation_[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#read-heroes-path-operation "Permanent link")

では、すべてのヒーローを読むために、別の**パス操作**を追加してみましょう。

```py
# Code above omitted 👆

app = FastAPI()

@app.on_event("startup")
def on_startup():
    create_db_and_tables()


@app.post("/heroes/")
def create_hero(hero: Hero):
    with Session(engine) as session:
        session.add(hero)
        session.commit()
        session.refresh(hero)
        return hero


@app.get("/heroes/")

def read_heroes():
    with Session(engine) as session:
        heroes = session.exec(select(Hero)).all()
        return heroes

```
クライアントが **パス** `/heroes/` に `GET` HTTP **操作** でリクエストを送ると、データベースからヒーローを取得して返すこの関数を実行します。

## One Session per Request[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#one-session-per-request "Permanent link")

操作のグループごとにSQLModelの**session**を使用し、他の関係のない操作が必要な場合は別のセッションを使用するべきだということを覚えていますか？

ここではそれがより明確になります。

通常、ほとんどの場合、リクエストごとに**1つのセッション**を使用するべきです。

いくつかの孤立したケースでは、内部に新しいセッションを持ちたいと思うかもしれません、つまり、**1つのリクエストに対して1つ以上のセッション**を持つことになります。

しかし、異なるリクエスト間で同じセッションを共有することは **ありません** 。

この単純な例では、**パス操作関数**で新しいセッションを手動で作成するだけです。

今後の例では、[FastAPI Dependency](https://fastapi.tiangolo.com/tutorial/dependencies/)を使って**session**を取得し、他の依存関係と共有したり、テスト中に置き換えることができるようにします。🤓

## Run the **FastAPI** Application[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#run-the-fastapi-application "Permanent link")

これでFastAPIアプリケーションを実行する準備が整いました。

すべてのコードを `main.py` というファイルに入れてください。

そして、それを **Uvicorn** で実行します。

```terminal
uvicorn main:app

INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [28720]
INFO:     Started server process [28722]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
```

#### infomation
コマンド `uvicorn main:app` は以下を参照しています。
- `- main`: ファイル `main.py` (Python の「モジュール」).
- `- app`: `main.py` の中で `app = FastAPI()` という行で作成されたオブジェクトです。

#### Uvicorn `--reload`[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#uvicorn-reload "Permanent link")

開発中に（開発中に限り）、Uvicorn に `--reload` というオプションを追加することもできます。これはコードに変更を加えるたびにサーバーを再起動するもので、こうすることでより早く開発を進めることができるようになります。🤓

```terminal
uvicorn main:app --reload

INFO:     Will watch for changes in these directories: ['/home/user/code/sqlmodel-tutorial']
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
INFO:     Started reloader process [28720]
INFO:     Started server process [28722]
INFO:     Waiting for application startup.
INFO:     Application startup complete.

```
必要以上にリソースを消費したり、エラーが発生しやすくなるため、運用時には絶対に`--reload`を使用しないようにしてください。

## Check the API docs UI[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#check-the-api-docs-ui "Permanent link")

これで、ブラウザでそのURLに行くことができます `http://127.0.0.1:8000`。ルートパス `/` に対して _path operation_ を作成していないので、その URL だけでは "Not Found" エラーが表示されるだけです... その "Not Found" エラーは、FastAPI アプリケーションによって生成されます。

しかし、パス `/docs` にある **自動的に生成されたインタラクティブ API ドキュメント** に行くことができます。[http://127.0.0.1:8000/docs](http://127.0.0.1:8000/docs). ✨

この **自動 API ドキュメント UI** は、上で定義した _path_ をその _operations_ と共に持っており、**path 操作** が受け取るデータの形状を既に知っていることがわかります。

![Interactive API docs UI](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1025335/cee8b154-2259-9dad-c659-b25b92df3632.png)


## Play with the API[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#play-with-the-api "Permanent link")

実際にボタンをクリックすることができます 試しに、**Create Hero** _path operation_でいくつかのヒーローを作成するリクエストを送ってみましょう。

そして、**Read Heroes** _path operation_でそれらを返してもらうことができます。

![Interactive API docs UI reading heroes](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1025335/964b4758-f160-96de-90bd-b8684fccb3ed.png)


## Check the Database[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#check-the-database "Permanent link")

これで、ターミナルに戻ってCtrl+Cを押すことで、そのUvicornサーバーを終了させることができます。

そして、**DB Browser for SQLite**を開いてデータベースをチェックし、データを調べて、確かにヒーローを保存したことを確認してください。🎉

![DB Browser for SQLite showing the heroes](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1025335/5535af56-8d39-6be6-8710-b47ecc7fc906.png)


## Recap[¶](https://sqlmodel.tiangolo.com/tutorial/fastapi/simple-hero-api/#recap "Permanent link")

これはもう、ヒーローのデータベースと対話するためのFastAPI **web API** アプリケーションです。🎉

改良や拡張できる点がいくつかあります。例えば、私たちはデータベースに各新しいヒーローのIDを決定してもらいたいのですが、ユーザーがそれを送信することを許可したくありません。

これらの改善はすべて次の章で行います。🚀