SQLModelとは 〜SQLModelインストール・DB接続・データ追加・データ取得まで〜

## SQLModelとは
[SQLModel](https://sqlmodel.tiangolo.com/
)とは、PythonからSQLを操作するためのライブラリです。
特徴として、直感的で使いやすい点、Pythonの型アノテーションをベースに、Pydantic と SQLAlchemy を備えている点、FastAPI アプリケーションで SQL データベースとの対話を簡素化するために設計されている点が挙げられます。また、FastAPIと同じ開発者によってSQLModelは開発されました。

https://github.com/tiangolo/sqlmodel


## SQLModelをインストールする

pipでインストール可能です。

```
pip install sqlmodel
```

## SQLModelのモデルを作成する

SQLModelのモデルを作成するコードは以下のように書きます。

```main.py
# https://github.com/tiangolo/sqlmodel
from typing import Optional
from sqlmodel import Field, SQLModel


class User(SQLModel, table=True):
    user_id: Optional[int] = Field(primary_key=True)
    user_name: str
        
user_1 = User(user_id=1, user_name="aiacademy")
```

UserクラスはSQLModelモデルで、PythonコードにおけるSQLテーブルに相当します。
Userクラスの各属性は、テーブルの各カラムに相当します。

user_1のコードにより、テーブルの各行がモデルのインスタンスとして作成されることになります。

**補足**

既に存在するテーブルがメタデータインスタンスに定義されている場合に、SQLModelでは以下のようなエラーを出力します。

```
InvalidRequestError: Table 'user' is already defined for this MetaData instance.  
Specify 'extend_existing=True' to redefine options and columns on an existing Table object.
```

この場合、Userモデル内に、**__table_args__ = {'extend_existing': True}**の記載をするとエラーを回避できます。

```
class User(SQLModel, table=True):
    __table_args__ = {'extend_existing': True} 
    user_id: Optional[int] = Field(primary_key=True)
    user_name: str
```



## データベースへ書き込みを行う

データを追加するには、create_engine()でsqliteに接続後、SQLModel.metadata.create_all(engine)の記述でメタデータを作成します。

その後、add()とsession.commit()にてデータを追加します。

まず、sqlmodelから、新規にSessionとcreate_engineを読み込みます。

以下のようにコードを書き換えてください。

```main.py
from typing import Optional
from sqlmodel import Field, Session, SQLModel, create_engine # 追加

class User(SQLModel, table=True):
    user_id: Optional[int] = Field(primary_key=True)
    user_name: str

user_1 = User(user_id=1, user_name="aiacademy")
engine = create_engine("sqlite:///database.db")

SQLModel.metadata.create_all(engine)

with Session(engine) as session:
    session.add(user_1)
    session.commit()
```

main.pyを実行後、user_1オブジェクトから、SQLiteデータベースを作成出来ました。
もう一件、別のデータも以下のように追加することが可能です。

```main.py
user_2 = User(user_name="aiacademy_bootcamp")

engine = create_engine("sqlite:///database.db")

SQLModel.metadata.create_all(engine)

with Session(engine) as session:
    session.add(user_2)
    session.commit()
```

### ***補足：セッションについて***

セッションは、エンジンからの接続などのリソースを保持しているため、セッションを使用が終わった後は、セッションを閉じる必要があります。
セッションを閉じることで、リソースを解放し、クリーンアップし終了します。

本来は、`session.close()`のコードが必要ですが、withを用いた場合、`session.close()`は不要です。

```main.py
user_2 = User(user_name="aiacademy_bootcamp")

engine = create_engine("sqlite:///database.db")

SQLModel.metadata.create_all(engine)

with Session(engine) as session:
    session.add(user_2)
    session.commit()
    # session.close() # この行は不要
```

withを用いない場合は、`session.close()`が必要で、以下のように書く必要があります。

```
user_2 = User(user_name="aiacademy_bootcamp")

engine = create_engine("sqlite:///database.db")

SQLModel.metadata.create_all(engine)

session = Session(engine)
session.add(user_2)
session.commit()
session.close() # この行は必要
```


## 保存したデータを取得する

保存したデータを取得するために、`select`と`where`を用いて以下のように書けます。

```get_data.py
from typing import Optional
from sqlmodel import Field, Session, SQLModel, create_engine, select
import warnings
warnings.filterwarnings('ignore')

class User(SQLModel, table=True):
    user_id: Optional[int] = Field(primary_key=True)
    user_name: str

engine = create_engine("sqlite:///database.db")
name = "aiacademy"

with Session(engine) as session:
    statement = select(User).where(User.user_name == name)
    user = session.exec(statement).first()
    print(user)
```

上記のコードを実行すると以下のように出力されれば成功です。

```
user_name='aiacademy' user_id=1
```

一方で、存在しないデータを取得した場合、Noneが返されます。

```
name = "python"

with Session(engine) as session:
    statement = select(User).where(User.user_name == name)
    user = session.exec(statement).first()
    print(user)
```

**出力結果**

```
None
```

# オブジェクトのリストを取得する
Userオブジェクトをリストで全て取得したい場合には`all()`を利用します。

```
from typing import Optional
from sqlmodel import Field, Session, SQLModel, create_engine, select
import warnings
warnings.filterwarnings('ignore')

class User(SQLModel, table=True):
     # __table_args__ = {'extend_existing': True}
     user_id: Optional[int] = Field(primary_key=True)
     user_name: str
        
engine = create_engine("sqlite:///database.db")

with Session(engine) as session:
    statement = select(User)
    users = session.exec(statement).all()
    print(users)
```

上記プログラムを実行すると以下のような出力になります。

```
[User(user_name='aiacademy', user_id=1), User(user_name='aiacademy_bootcamp', user_id=2)]
```

先ほど追加した下記プログラムでは、それぞれのコードが何をしているかを手助けするために、変数に格納していますが、１行に書き換えることも出来ます。

**書き換え前**

```
with Session(engine) as session:
    statement = select(User)
    users = session.exec(statement).all()
    print(users)
```


**書き換え後**

```
with Session(engine) as session:
    users = session.exec(select(User)).all()
    print(users)
```


# Pythonを効率よく学ぶには？
Pythonを効率よく学ぶには、普段からPythonを利用している現役のデータサイエンティストや機械学習エンジニアに質問できる環境で学ぶことです。
質問し放題かつ、体系的に学べる動画コンテンツでデータ分析技術を学びたい方は、オンラインで好きな時間に勉強できる[AI Academy Bootcamp](https://aiacademy.jp/bootcamp)がオススメです。受講料も業界最安値の35,000円（６ヶ月間質問し放題＋オリジナルの動画コンテンツ、テキストコンテンツの利用可能）なので、是非ご活用ください。

[![bootcamp_ad_72ppi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/171715/b4e1cc19-264c-7c56-28fb-1ceadb5c60ab.png)](https://aiacademy.jp/bootcamp)