SQLAlchemyのSession生成方法

# 概要
SQLAlchemyのSessionの生成方法はいくつかあります。
ここではそれらの生成方法を一つ一つ説明していきます。

調査した時の各バージョン

* SQLAlchemy : 1.3.3

# Session生成方法の種類
Sessionの生成方法には以下のような種類があります。

* Sessionクラスによる生成（基本）
* sessionmakerによる生成
* scoped_sessionによる生成
* scoped_sessionによる生成（ORM編）

# ①Sessionクラスによる生成（基本）
基本的な生成方法です。
SessionクラスからSessionインスタンスを生成するんだから一番わかりやすいですね。
例えばこんな感じで生成します。

```python:main.py
from sqlalchemy import create_engine
from sqlalchemy.orm import Session

engine = create_engine(～略～)

# Sessionインスタンスの生成
session = Session(
    autocommit = False,
    autoflush = True,
    bind = engine)
```
引数は大体この3つを指定していることが多いです。但し、`autocommit=False`、`autoflush=True`はデフォルト値である為、省略も可能です。

注意点としては、`session = Session()`を実行する度に新しいSessionが生成されるということでしょうか。

# ②sessionmakerによる作成
sessionmakerクラスを使用してSessionを生成します。
公式のドキュメントでは、Sessionクラスによる生成よりこちらを推しています。

```python:settings.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker

engine = create_engine(～略～)

# この時点ではまだSessionインスタンスは生成されていない
# Session変数に格納されているのは実はsessionmakerインスタンス
Session = sessionmaker(
    autocommit = False,
    autoflush = True,
    bind = engine)
```
```python:main.py
from settings import Session

# Sessionインスタンスの生成
# settings.pyで指定した引数でSessionが生成される
session = Session()
```
この生成方法であればSessionを生成する際に引数を指定する必要がなく、常に同じ引数でSessionが生成されます。但し、`session = Session()`を実行する度に新しいSessionが生成されるのはSessionクラスによる生成と同様です。

※ただ、実際に書くコードでは毎回`session = Session()`はせずに`settings.py`辺りで一回だけSessionを生成してそれを使い回すことが多いと思うので、そうすると①と②はあまり違わない気もします。。

# ③scoped_sessionによる生成
scoped_sessionという内部にSession管理レジストリを内包したクラスによる生成方法です。

```python:settings.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session

engine = create_engine(～略～)

# この時点ではまだSessionインスタンスは生成されていない
# Session変数に格納されているのは実はscoped_sessionインスタンス
Session = scoped_session(
            sessionmaker(
                autocommit = False,
                autoflush = True,
                bind = engine))   
```
```python:main.py
from settings import Session

# Sessionインスタンスの生成
# settings.pyで指定した引数でSessionが生成される
session01 = Session()

# session01とsession02は同一のSession
session02 = Session()
```
sessionmakerインスタンスを内包したscoped_sessionインスタンスを生成して、そのインスタンスからSessionを生成しています。sessionmakerとの違いは、`Session()`を何回実行しても同一のSessionが返されるという点です。
ただ、この方法もこれだけだとsessionmaker等と比較して凄く良いかと言われると、そこまででもないかなって感じです。（②のところで書いたように、同一のSessionを使い回したいだけなら`settings.py`で予め生成しておけばいいですし。。）

# ④scoped_sessionによる生成（ORM編）
scoped_sessionが効果を発揮するのはやはりORMと連携させた時です。

```python:settings.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base

engine = create_engine(～略～)

session = scoped_session(
            sessionmaker(
                autocommit = False,
                autoflush = True,
                bind = engine))

Base = declarative_base()
# 予めテーブル定義の継承元クラスにqueryプロパティを仕込んでおく
Base.query = session.query_property()
```
```python:model.py
from sqlalchemy import Column, Integer, String
from settings import Base

# UserクラスはBaseを継承しているのでqueryプロパティを持つ
# XXX: ここで直接queryプロパティを仕込んでも良い
class User(Base):
    __tablename__ = 'user_list'
    id = Column('id', Integer, primary_key=True)
    name = Column('name', String(100))
```
```python:main.py
from settings import session
from model import User

# Seesionを使ってるように見えないが内部的に使ってる
result = User.query.filter_by(id == 1).all()

# 特にcommitに意味はないけどサンプルコードとして
session.commit()
```
上記コードの`User.query.～`のように、テーブルクラスを起点としてコード(select文)が書けるようになります。（ORMっぽい・・ぽくない？）
`User.query.～`は、`session.query(User).～`と同義です。

また、「③scoped_sessionによる生成」で`session01 = Session()`と書きましたが、実は`session01 = Session()`と明示的に記述するまでもありません。上記コードの`session.commit()`のようにいきなりSessionを使い始めることができます。`session.commit()`は`session().commit()`と同義です。

# まとめ
ORMを使うなら④のような構成にしておくと柔軟にコードを書くことができると思います。逆に生SQLを実行するちょっとしたツールくらいなら①や②で十分でしょう。

とりあえず調べた限りではこれくらいでしたが、他の生成方法を知ってる方がいましたらご教示くださいー