SQLAlchemyをGoogle Colaboratoryで簡単に試してみる

# はじめに
ORMとは？を学習するのに手を動かした方が良いと思い、SQLAlchemyをGoogle Claboratoryでサクッと試してみました。
間違っている箇所などありましたら指摘してもらえると嬉しいです。

# 目次
[1. ORMとは？](#1-ormとは)
[2. SQLAlchemyとは？](#2-sqlalchemyとは)
[3. 環境設定とインポート](#3-環境設定とインポート)
[4. テーブルの定義と作成](#4-テーブルの定義と作成)
[5. 複数ユーザーの登録](#5-複数ユーザーの登録)
[6. 新規ユーザーの追加](#6-新規ユーザーの追加)
[7. ユーザー情報の更新と削除](#7-ユーザー情報の更新と削除)
[8. 現在のユーザーを表示](#8-現在のユーザーを表示)
[9. 他の書き方と比較](#9-他の書き方と比較)
[10. まとめ](#まとめ)

## 1. ORMとは？
ORM（Object-Relational Mapping）は、プログラムのオブジェクトとデータベースのテーブルをつなげる技術です。ORMを使うと、データベース操作を慣れているプログラミング言語で書けるようになります。SQL文を直接書かずに済むので、コードがよりシンプルで読みやすくなります。

## 2. SQLAlchemyとは？
SQLAlchemyは、SQL構文を直接書かずにオブジェクト指向でデータベース操作ができる強力なツールです。柔軟なクエリ構築、データベース間の移植性、トランザクション管理、パフォーマンス最適化の機能が豊富で、シンプルなCRUD操作から複雑な結合まで幅広く対応します。
以下のRDBをサポートしています
[公式ドキュメント](https://docs.sqlalchemy.org/en/20/dialects/index.html)
- Microsoft SQL Server
- MySQL / MariaDB
- Oracle
- PostgreSQL
- SQLite

## 3. 環境設定とインポート
まずは、Colab上でSQLAlchemyをインストールします。
```
!pip install SQLAlchemy
```
次に、必要なモジュールをインポートします。

```python
from sqlalchemy import create_engine, Column, Integer, String, Sequence  # データベースエンジンの作成とカラム定義
from sqlalchemy.ext.declarative import declarative_base  # 基底クラスを作成するためのメソッド
from sqlalchemy.orm import sessionmaker  # セッションを作成するためのファクトリメソッド
```
### インポートしたメソッドとクラスの説明
- `create_engine`: データベースに接続するためのエンジンを作る関数です。
- `Column`: テーブルのカラム（列）を定義するクラスです。
- `Integer`: 整数型のカラムを定義するためのデータ型です。
- `String`: 文字列型のカラムを定義するためのデータ型です。
- `Sequence`: 自動で一意の値を生成するシーケンスを定義するためのクラスです。
- `declarative_base`: ORMで使う基底クラスを作る関数です。これを使って、テーブルをPythonのクラスとして定義します。
- `sessionmaker`: データベースとのやり取りを管理するセッションオブジェクトを作る関数です。

## 4. テーブルの定義と作成
ユーザー情報を管理するテーブルを定義してみます。

```python
# Baseクラスの作成
Base = declarative_base()

# Userクラスの定義
class User(Base):
    __tablename__ = 'users'  # テーブル名を指定
    id = Column(Integer, Sequence('user_id_seq'), primary_key=True)  # プライマリキーの定義
    name = Column(String(50))  # 名前カラムの定義
    job = Column(String(50))  # 職業カラムの定義

# データベースエンジンの作成（SQLiteを使用）
engine = create_engine('sqlite:///:memory:', echo=True)

# テーブルの作成
Base.metadata.create_all(engine)
```
## 5. 複数ユーザーの登録
まずは`add_all()`を使用して複数のユーザーを登録してみます。

```python
# セッションの作成
Session = sessionmaker(bind=engine)
session = Session()

# 複数のユーザーを追加
new_users = [
    User(name='田中', job='戦士'),
    User(name='ボブ', job='魔術師'),
    User(name='スティーブ', job='商人')
]

# 複数のユーザーをセッションに追加
session.add_all(new_users)
session.commit() # 変更をデータベースに保存
```
## 6. 新規ユーザーの追加
`add()`を使って新しいユーザーを追加してみます。

```python
# 新しいユーザーの追加
new_user = User(name='アリス', job='僧侶')
session.add(new_user)
session.commit()
```

## 7. ユーザー情報の更新と削除
### ユーザーの職業を更新する
- `query()`メソッドは、データベースからデータを取得するためのクエリを作成するために使用されます。session.query(User)のように使用すると、Userテーブル（モデル）に対するクエリオブジェクトを作成します。このオブジェクトを使って、フィルタリング、並び替え、集計などの操作を行うことができます。

- `filter_by()`メソッドは、特定の条件に基づいてデータをフィルタリングするために使用されます。`session.query(User).filter_by(name='田中')`のように使うと、`User`テーブルから`name`が田中のレコードを取得するためのフィルタが設定されます。条件はキーワード引数形式で指定します。これはWHERE句に相当する部分を定義します。

- `update()`メソッドは、指定された条件に一致するレコードを一度に更新するために使用されます。このメソッドを使うと、マッチしたすべてのレコードが一度に更新されます。

```python
# 職業の更新
session.query(User).filter_by(name='田中').update({"job": "パラディン"})
session.commit()
```
### ユーザーの削除
`delete()`を使って削除できます。`delete()`は条件に一致するすべてのレコードを削除します。

```python
# 削除を実行
delete_user = session.query(User).filter_by(name='スティーブ').delete()
session.commit()
```

## 8. 現在のユーザーを表示
最後に、現在登録されているすべてのユーザーを表示しましょう。

```python
# 現在のユーザーを表示
users = session.query(User).all()
print("現在のユーザー:")
for user in users:
    print(f"名前: {user.name}, 職業: {user.job}")
```
以下のように出力され、変更や削除が無事に行われているのが確認できます。
```python
# 出力
現在のユーザー:
名前: 田中, 職業: パラディン
名前: ボブ, 職業: 魔術師
名前: アリス, 職業: 僧侶
```
## 9. 他の書き方と比較
純粋なSQL文を使用してPythonからデータベースを操作する場合は、sqlite3やpsycopg2などのPython標準ライブラリやサードパーティライブラリを使用します。これらを使ってデータベースに直接接続し、SQL文を実行します。以下は、sqlite3ライブラリを使ってSQLiteデータベースに接続し、ユーザーの職業を更新する例です。

純粋なSQL文を使用する場合の例（sqlite3を使用）
```python
import sqlite3

# データベースに接続（'example.db'というファイルを使った場合）
conn = sqlite3.connect('example.db')  # データベースファイルに接続
cursor = conn.cursor()  # カーソルオブジェクトを作成

# SQL文を直接実行してユーザーの職業を更新
update_query = "UPDATE users SET job = ? WHERE name = ?"
cursor.execute(update_query, ("パラディン", "田中"))  # パラメータを使ってSQLクエリを実行

# トランザクションをコミットして終了
conn.commit()  # トランザクションをコミットして変更を確定
conn.close()  # データベース接続を閉じる
```
純粋なSQL文を使用する場合の手順
データベース接続の開始: sqlite3.connect('example.db')でデータベースに接続し、connオブジェクトを取得します。

カーソルの作成: conn.cursor()でカーソルオブジェクトを作成し、データベース上でSQL操作を行う準備をします。

SQL文の実行: cursor.execute()メソッドで、UPDATE文を実行してユーザーの職業を更新します。パラメータは?プレースホルダを使用してバインディングし、安全にSQLを実行します。

トランザクションのコミットと接続の終了: conn.commit()でトランザクションをコミットしてデータベースに変更を反映し、conn.close()で接続を閉じます。

これを以下の2行で実行できるのはかなり便利ですね！
```python
session.query(User).filter_by(name='田中').update({"job": "パラディン"})
session.commit()
```
この更新処理は他にも一度オブジェクトを取得する方法や
```python
# まずはオブジェクトを取得
user = session.query(User).filter_by(name='田中').first()

# オブジェクトの属性を更新
if user:
    user.job = "パラディン"
    session.commit()  # 変更をコミット
```
`execute()`メソッドを使って直接SQL文を書く方法もあるようです。
```python
# SQL文を直接実行して更新
session.execute("UPDATE users SET job = :job WHERE name = :name", {"job": "パラディン", "name": "田中"})
session.commit()
```
`merge()`メソッドというのもあり、`merge()`メソッドはデータベース内に同じプライマリキーを持つオブジェクトがある場合、そのオブジェクトを更新します。ない場合は、新しく挿入します。
```python
# 既存のユーザーを更新または新しいユーザーを作成（マージ）
user = User(name='田中', job='パラディン')
session.merge(user)
session.commit()
```
今回はクエリが一度に実行できて、簡潔な`update()`メソッドがわかりやすいなと思いましたが、その時の要件などにより使い分けると良いかもしれません。


# まとめ
以上SQLAlchemyをGoogle Colaboratoryで試してみました。
実業務とは違うかもしれませんが、ORMをどういう風に使うのか手軽に試せてレスポンスもすぐに見ることができるので練習環境としてはとても良いと感じました。
是非試してみてください。