Alembicにてautogenerateでマイグレーションスクリプトを作成するまでの手順

## 目次
1. [行いたいこと](#1行いたいこと) 
2. [動作環境](#2動作環境)
3. [手順](#3手順)
    3-1. [alembicのインストール](#3-1alembicのインストール)
    3-2. [alembic環境の作成](#3-2alembic環境の作成)
    3-3. [alembic.iniの編集](#3-3alembiciniの編集)
    3-4. [env.pyの編集](#3-4envpyの編集)
    3-5. [マイグレーションスクリプトの作成](#3-5マイグレーションスクリプトの作成)
    3-6. [マイグレーションの実行](#3-6マイグレーションの実行)
4. [番外編(環境変数をコマンド実行時に設定する方法)](#4番外編環境変数をコマンド実行時に設定する方法)
5. [参考](#5参考)

## 1.行いたいこと
Alembicはsqlalchemyのマイグレーションツールです。
今回はAlembicを導入し、sqlalchemyを用いて作成したデータベースのバージョン管理を行い、複数環境間での一貫性を保てるようにします。
Alembicを使用するにあたって、以下の2点を満たせるように環境を作成したいと思います。
- マイグレーションスクリプトは手動で作成するのではなく、自動で作成できる状態にする
- DBの接続先をハードコードするのではなく、環境変数を使って設定できるようにする

既にsqlalchemyを使用してテーブルを作成しているため、Alembic環境を作成するところからの
手順をメモとして残したいと思います。

## 2.動作環境
alembic 1.13.3
SQLAlchemy 2.0.36
docker 25.0.3

## 3.手順
- コマンド実行前のフォルダ構成
```
backend/ 
└─db/ 
    ├─__init__.py
    ├─database.py
    └─db_model.py
└─.env
```

### 3-1.alembicのインストール
今回はdocker環境で行うため、仮想環境は作成せず、インストールします。
```sh
pip install alembic
```

### 3-2.alembic環境の作成
はじめに、以下のコマンドを実行し、alembicの環境を作成(初期化)します。
```sh
# initの後でフォルダ名を指定
alembic init alembic
```
今回はフォルダ名をalembicとしましたが、他の名前でも問題ありません。

- コマンド実行後のフォルダ構成
```
backend/ 
└─db/ 
    ├─__init__.py
    ├─database.py
    └─db_model.py
└─alembic/           #新規
    ├─versions/      #新規
    ├─README         #新規
    ├─script.mako.py #新規
    └─env.py         #新規
└─alembic.ini        #新規
└─.env
```

このように、alembic.iniファイルとalembicフォルダが作成されます。
almebicフォルダ内には「env.py」「README」「script.mako.py」の3つのファイルと「versions」フォルダが作成されており、この時点でversionsフォルダは空になっています。

### 3-3.alembic.iniの編集
alembic.iniファイルにて変数「sqlalchemy.url」の値を書き換え、DBの接続先を指定します。
環境変数は%(〇〇)sのように指定することができます。
```alembic.ini
sqlalchemy.url = mysql+pymysql://%(MYSQL_USER)s:%(MYSQL_PASSWORD)s@%(MYSQL_HOST)s/%(MYSQL_DATABASE)s
```


### 3-4.env.pyの編集
.envにて環境変数を設定します。
.envはセキュリティ上gitなどへコミットしないようにしてください。
```.env
MYSQL_USER=user1
MYSQL_PASSWORD=password
MYSQL_HOST=localhost
MYSQL_DATABASE=db
```

以下の文を追加し、環境変数を読み込めるようにします。
```env.py
config.set_section_option("alembic", "MYSQL_USER", os.getenv("MYSQL_USER"))
config.set_section_option("alembic", "MYSQL_PASSWORD", os.getenv("MYSQL_PASSWORD"))
config.set_section_option("alembic", "MYSQL_HOST", os.getenv("MYSQL_HOST"))
config.set_section_option("alembic", "MYSQL_DATABASE", os.getenv("MYSQL_DATABASE"))
```

続いて、Baseをインポートし、モデルのメタデータを取得できるようにします。
```env.py
from db.database import Base

target_metadata = Base.metadata
```

加えて、以下のインポート文を追加することでBaseを継承して作成したテーブルのメタデータを取得できるようにします。
以下のように、db/\__init__.pyにてdb/db_model.pyで定義したモデルを全てインポートし、

```db/__init__.py
from db.db_model import User, Todo
```

env.pyにてdb_modelをインポートします。

```env.py
import db.db_model 
```
各モデルをインポートしない場合、テーブルのメタデータを読み取れず、マイグレーションスクリプトを適切に自動生成できないことがあるようです。
実際に、列名を変更し、自動でのスクリプト作成を実行しましたが、全てのテーブルを削除するスクリプトが作成されてしまいました。(これはテーブルのメタデータを読み取れていないことが原因のようです。)
そのため、上記のように各モデルをenv.pyへインポートしました。



### 3-5.マイグレーションスクリプトの作成
以下のコマンドを実行します。
```sh
# メッセージの内容は変更内容がわかるように置き換えてください
alembic revision --autogenerate -m “メッセージ”
```
今回は自動でスクリプトを作成するため、「--autogenerate」をつけています。
**自動で作成する場合、手動での修正が必要な場合がありますので、内容を必ず確認してください**
手動で作成する場合は「--autogenerate」をつけずに実行してください。
このコマンド実行後、マイグレーションスクリプトがalembic/versions配下に作成されます。

### 3-6.マイグレーションの実行
以下のコマンドを実行します。
```sh
# 最新のバージョンへアップグレード
alembic upgrade head

# マイグレーションを指定したい場合
# 現在のバージョンから2つ先のバージョンへアップグレード
alembic upgrade +2
```

現在のバージョンや履歴の確認、ダウングレードするコマンドは以下の通りです。
```sh
# 現在のバージョンを確認
alembic current

# マイグレーション履歴の確認
alembic history

# 1つ前のバージョンに戻す
alembic downgrade -1
```

## 4.番外編(環境変数をコマンド実行時に設定する方法)
環境変数を.envで使用しない場合など、コマンド実行時に設定する場合は以下の方法で実行できます。
```sh
# dockerを使用する場合
# コンテナ名などは適宜置き換えてください
docker-compose exec -e 変数名=値 コンテナ名　alembic revision --autogenerate -m “メッセージ”

# dockerを使用しない場合(Linux)
export 変数名=値
alembic revision --autogenerate -m “メッセージ”
```




## 5.参考
https://alembic.sqlalchemy.org/en/latest/tutorial.html
https://alembic.sqlalchemy.org/en/latest/autogenerate.html
https://docs.docker.jp/compose/environment-variables.html