alembicのマイグレーション方法メモ（Postgres）

# 目的
alembic の使い方が意外とめんどくさかったので自分用メモ作成。

# 前提条件
DockerのPython環境。
Poetryがインストールされていること。

ファイル構成は下記の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/691111/69cb4541-f0d0-b7ac-f845-c65243f407cf.png)



# マイグレーション方法
## 初回時

alembicをインストール
``` .bash
poetry add alembic
```

alembicからPostgresに接続するためpsycopg2-binaryをインストール。
（本番環境ではpsycopg2にする？）
``` .bash
poetry add psycopg2-binary
```

alembicを初期化
``` .bash
poetry run alembic init alembic
```

alembic.iniファイルを開き、sqlalchemy.urlにデータベースURLを設定します。
``` alembic.ini
# user、password、sample_dbなどは環境に応じた値に設定する
sqlalchemy.url = postgresql+psycopg2://{user}:{password}@db:5432/{sample_db}

```
app/database.pyを作成。
``` database.py
from sqlalchemy.ext.declarative import declarative_base  #

# Baseクラスの定義
Base = declarative_base()
```
app/models/sample_model.pyを作成。
``` sample_model.py
from sqlalchemy import Column, Integer, String
from app.database import Base

class SampleModel(Base):
    __tablename__ = "sample_table"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)
    description = Column(String)
```
app/models/__init__.pyを作成。
``` __init__.py
# app/models/__init__.py

# 各モデルをインポート
from .sample_model import SampleModel
# 他のモデルがある場合も同様に追加
# from .other_model import OtherModel

# Alembicや他のスクリプトがapp.modelsをインポートするだけで全モデルを認識できるようにする
__all__ = [
    "SampleModel",
    # "OtherModel"  # 他のモデルを追加する場合もここに名前を追加
]
```

alembic/env.pyの下記部分を変更。
インポートするがコードしては使用しないため、ruffなどの静的解析ツールで勝手に削除される場合がある。
``` env.py
from app.database import Base  # Baseをインポート追加
import app.models  # __init__.pyで設定した全てのモデルを自動インポート

target_metadata = Base.metadata

```

マイグレーションファイルを自動作成。
``` .bash
poetry run alembic revision --autogenerate -m "Initial migration"
```

マイグレーションファイルを実行して、データベースを作成します。
``` .bash
poetry run alembic upgrade head
```

## 2回目以降
app/models/sample_model2.pyを作成。（中身は省略）

app/models/__init__.pyにsample_model2.pyを追加。
``` __init__.py
# app/models/__init__.py

# 各モデルをインポート
from .sample_model import SampleModel
from .sample_model2 import SampleModel2
# 他のモデルがある場合も同様に追加
# from .other_model import OtherModel

# Alembicや他のスクリプトがapp.modelsをインポートするだけで全モデルを認識できるようにする
__all__ = [
    "SampleModel",
    "SampleModel2",
    # "OtherModel"  # 他のモデルを追加する場合もここに名前を追加
]
```

alembic/env.pyはそのまま。

以下のコマンドを実行してマイグレーションファイルを自動生成。
``` .bash
poetry run alembic revision --autogenerate -m "Add sample_model2"
```

マイグレーションファイルを実行して、データベースを更新します。
``` .bash
poetry run alembic upgrade head
```
## 既存のモデルを修正した場合
任意のModelファイルを修正。

モデルを修正後に下記を実行。
``` .bash
poetry run alembic revision --autogenerate -m "Modify existing model"
```
マイグレーションファイルを実行して、データベースを更新します。
``` .bash
poetry run alembic upgrade head
```










## テーブルを削除する場合
app/models/sample_model2.pyなどを削除

app/models/__init__.pyのsample_model2.pyに関する記述を削除。
``` __init__.py
# app/models/__init__.py

# 各モデルをインポート
from .sample_model import SampleModel # ここを削除
from .sample_model2 import SampleModel2 # ここを削除
# 他のモデルがある場合も同様に追加
# from .other_model import OtherModel

# Alembicや他のスクリプトがapp.modelsをインポートするだけで全モデルを認識できるようにする
__all__ = [
    "SampleModel", # ここを削除
    "SampleModel2", # ここを削除
    # "OtherModel"  # 他のモデルを追加する場合もここに名前を追加
]
```

alembic/env.pyはそのまま。

以下のコマンドを実行してマイグレーションファイルを自動生成。
``` .bash
poetry run alembic revision --autogenerate -m "Delete sample_model2"
```

マイグレーションファイルを実行して、データベースを更新します。
``` .bash
poetry run alembic upgrade head
```

↑の手順で外部キー参照などでエラーが発生する場合、生成されたマイグレーションファイルを編集して再度実行する。


## テーブルを全て削除して作り直す
DBで下記のSQLコマンドを実行して全てのテーブルを削除。
``` .bash
DO $$ 
BEGIN
    EXECUTE (
        SELECT string_agg('DROP TABLE IF EXISTS "' || tablename || '" CASCADE;', ' ')
        FROM pg_tables
        WHERE schemaname = 'public'
    );
END $$;
```
alembic/versionsの全てのファイルを削除。


以下のコマンドを実行してマイグレーションファイルを自動生成。
``` .bash
poetry run alembic revision --autogenerate -m "recreate migration"
```

マイグレーションファイルを実行して、データベースを更新します。
publicは必要に応じて書き換える可能性あり。
``` .bash
poetry run alembic upgrade head
```


## その他
マイグレーションを１つ戻す
``` .bash
poetry run alembic downgrade -1
```

特定のリビジョンに戻す
``` .bash
poetry run alembic downgrade <revision_id>
```