SQLAlchemyの基本的な使い方

# SQLAlchemyとは
SQLAlchemyとは，Pythonの中でよく利用されているORMの1つ．
ORMとは，Object Relational Mapperのことで，簡単に説明すると，テーブルとクラスを1対1に対応させて，そのクラスのメソッド経由でデータを取得したり，変更したりできるようにする存在．
## ORMの利点とは
### 1. 異なるDBの違いを吸収してくれる
DBの種類によらず，同じソースコードで操作できるので，複数のDBを併用する場合や，DBを変更する場合にも，コードの書き換えの必要がない．
### 2. SQLを書かなくてもよい
MySQLや，SQLite，PostgreSQLなどのDBを操作するにはSQLを使うが，SQLAlchemyを使うと，SQLを直接記述することなしに，DBを"Pythonic"に操作できる．

# SQLAlchemyの使い方
簡単な流れとしては，どのDBにどうやって接続するか，設定を行う．(その設定内容を保持したものがエンジンと呼ばれる)
その後，マッピングを行い，セッションを作成する．
そして，そのセッションを使ってDB操作を行う．

## 1. DBエンジンを作成する

```python
from sqlalchemy import create_engine
engine = create_engine("{dialect}+{driver}://{username}:{password}@{host}:{port}/{database}?charset={charset_type})
```
のように書き，エンジンのインスタンスを作成する．各要素の説明は以下の表のとおり．

|要素&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;&emsp;|説明|
|:-------------------|:-------------------|
|dialect|DBの種類を指定する．sqlite, mysql, postgresql, oracle, mssqlとか．|
|driver|DBに接続するためのドライバーの指定をする．指定しなければ，"default" DBAPIになる．|
|username|DBに接続することができるユーザ名を指定する．|
|password|DBに接続するためのパスワードを指定する．|
|host|ホスト名を指定する．localhostとかIPアドレスとか．|
|port|ポート番号を指定する．指定しなければ，defaultのポート番号になるっぽい？|
|database|接続するデータベース名を指定する．|
|charset_type|文字コードを指定する．utf8とか．|

例えば，以下のような感じ．(driverとportとcharset_typeは指定していない．)

```python
engine = create_engine("mysql://scott:tiger@localhost/foo")
```

## 2. モデルクラスを作る(テーブル定義を書く)
まずは，モデルベースクラスを作る．

```python
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
```
そして，このベースクラスを拡張すると，ORMで扱えるモデルクラスになる．
例えば，以下のようにクラスを書けばよい．
また，モデルクラスを定義するときに，メソッドを追加することも可能である．

```python
from sqlalchemy.schema import Column
from sqlalchemy.types import Integer, String

class User(Base):
    __tablename__ = "user"  # テーブル名を指定
    user_id = Column(Integer, primary_key=True)
    first_name = Column(String(255))
    last_name = Column(String(255))
    age = Column(Integer)

    def full_name(self):  # フルネームを返すメソッド
        return "{self.first_name} {self.last_name}"
```
上で定義したのは以下の表のようなテーブルをマッピングさせたクラス．

|Field &emsp;&emsp;&emsp;&emsp;|Type &emsp;&emsp;&emsp;&emsp;&emsp;&emsp;|Null &emsp;|Key &emsp;|Default|Extra|
|:---|:---|:---|:---|:---|:---|
|user_id|int(11)|NO|PRI|NULL|auto_increment|
|first_name|varchar(255)|YES||NULL||
|last_name|varchar(255)|YES||NULL||
|age|int(11)|YES||NULL||
このテーブルをDBに作成するには，

```python
Base.metadata.create_all(engine)
```
これで，Baseを継承しているテーブル郡が一括して作成される．
詳しいテーブル定義の内容は，以下．
[SQLAlchemyでのテーブル定義](https://qiita.com/ariku/items/8ae90e4a73ef30dc4749)
### metadataとは
上のコードでmetadataという言葉が出てきたが，metadataとは，DBの様々な情報を保持しているオブジェクトのこと．
このmetadataを使うと，既存のDBからテーブル定義をもってきたりすることもできる．

## 3. セッションを作成する
SQLAlchemyはセッションを介してクエリを実行する．
そもそも，セッションとは，コネクションを確立してから切断するまでの一連の単位のこと．(DBとPythonコードを結びつける紐のようなものというイメージ)

セッションを作るクラスをsessionmakerで作る．(使うエンジンが一定なら，このとき指定する)

```python
from sqlalchemy.orm import sessionmaker
SessionClass = sessionmaker(engine)  # セッションを作るクラスを作成
session = SessionClass()
```
詳しい扱い方は以下．
[SQLAlchemyでのsessionの扱い方](https://qiita.com/ariku/items/14f49fe24395f1dd9412)
## 4. CRUD処理を行う
CRUDというのは，以下の機能をまとめた呼び方のこと．
・Create(新規作成)
・Read(読み取り)
・Update(更新)
・Destroy(削除)

セッションを閉じたり，明にcommit()しないとDBが更新されないので注意．

### INSERT
新規オブジェクトをsessionにadd()すると，INSERT対象になる．

```python
user_a = User(first_name="first_a", last_name="last_a", age=20)
session.add(user_a)
session.commit()
```

|user_id|first_name|last_name|age|
|:---|:---|:---|:---|
|1|a|a|20|

### SELECT
テーブルからデータを取り出すには，queryを使う．

```python
users = session.query(User).all()  # userテーブルの全レコードをクラスが入った配列で返す
user = session.query(User).first()  # userテーブルの最初のレコードをクラスで返す
```
詳しくは，参考文献．

### UPDATE
セッションから取り出したオブジェクトを変更すると，UPDATE対象になる．

```python
user_a = session.query(User).get(1)  # 上で追加したuser_id=1のレコード
user_a.age = 10
session.commit()
```
|user_id|first_name|last_name|age|
|:---|:---|:---|:---|
|1|a|a|10|

### DELETE
セッションから取り出したオブジェクトをdelete()すると，DELETE対象になる.

```python
user_a = session.query(User).get(1)
session.delete(user_a)
session.commit()
```
もしくは，検索条件にマッチするものを消すこともできる．

```python
session.query(User).filter(User.user_id=1).delete()
session.commit()
```
## 5. 既存のテーブルを使う場合
3では，新規のテーブルをマッピングしたクラスを作成したが，既存のテーブルをマッピングしたクラスを作りたいときもあるはず．
手順としては，Baseクラスにmetadataを渡し，\_\_tablename__を既存のテーブル名に合わせて，autoloadをTrueにすればよい．
Baseクラスにmetadataを渡す方法としては，Baseクラスを作るときにengineを渡す方法

```python
Base = declarative_base(bind=engine)
```
や，Baseクラスを作るときにmetadataを渡す方法

```python
from sqlalchemy.schema import MetaData
meta = MetaData(engine)
meta.reflect()  # metadataを取得， meta=MetaData(engine, reflect=True)と同じ
Base = declarative_base(metadata=meta)
```
や，Baseクラスを作ったあとに付与する方法

```python
Base = declarative_base()
Base.metadata.bind = engine
```
がある．
例えば，テーブル名がexisiting\_userというテーブルが存在していたとすれば．

```python
Base = declarative_base(bind=engine)

class Exisiting_user(Base):  # クラス名は何でもok
    __tablename__ = "exisiting_user"
    __table_args__ = {"autoload": True}
```
とすればよい．
# 参考文献
この記事は以下の情報を参考にして執筆しました．
・[公式ドキュメント](https://www.sqlalchemy.org/)
・[PlaySQLAlchemy: SQLAlchemy入門](https://www.slideshare.net/YasushiMasuda/playsqlalchemy-sqlalchemy)
・[SQLAlchemyでデータベース定義を楽したい](https://www.sw-mono.blog/entry/2019/02/24/145833)
・[【PythonのORM】SQLAlchemyで基本的なSQLクエリまとめ](https://qiita.com/ckern/items/a762b1bc0f192a55eae8)
・[sqlalchemyのautomapによる既存のDBからのmodelの生成について](https://pod.hatenablog.com/entry/2017/07/01/234830)