LoginSignup
0
0

【備忘録】PythonでMySQLのデータベースをマイグレーションする方法

Last updated at Posted at 2024-01-20

備考

Python:3.9
MySQL:8.0.31
動作環境:MacOS
開発環境:PyCharm

事前準備

データベース作成済み

1. PyCharmにて新規プロジェクトを作成する

2. プロジェクトを以下の構成になるようにファイルを作成する

フォルダ構成.
.
├── main.py
├── env.py
├── .env
├── requirements.txt
└── database
    ├── database.py
    └── models.py

3. requirements.txtファイルを以下の内容にする

requirements.txt
alembic
pytz
python-dotenv

4. ターミナルにてコマンドを入力してモジュールをインストールする

ターミナル.
pip install -r requirements.txt

5. .envファイルを以下の内容にする

.env
# DB接続情報
DATABASE = 'mysql'
DB_USER = 'root'
DB_PASSWORD = 'MySQLのパスワード'
DB_HOST = 'localhost'
DB_PORT = '3306'
DB_NAME = '対象のDB名'

6. env.pyファイルを以下の内容にする

env.py
import os
from dotenv import load_dotenv
from os.path import join, dirname

dotenv_path = join(dirname(__file__), '.env')
load_dotenv(dotenv_path)


class Env:
    # DB接続情報
    DATABASE = os.environ.get("DATABASE")
    DB_USER = os.environ.get("DB_USER")
    DB_PASSWORD = os.environ.get("DB_PASSWORD")
    DB_HOST = os.environ.get("DB_HOST")
    DB_PORT = os.environ.get("DB_PORT")
    DB_NAME = os.environ.get("DB_NAME")

7. database.pyファイルを以下の内容にする

database.py
from env import Env
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, scoped_session
from sqlalchemy.ext.declarative import declarative_base

# データベースに接続するための情報
database_url = '{}://{}:{}@{}:{}/{}?charset=utf8'.format(Env.DATABASE, Env.DB_USER, Env.DB_PASSWORD, Env.DB_HOST, Env.DB_PORT, Env.DB_NAME)
engine = create_engine(database_url, connect_args={"connect_timeout": 15}, echo=False, pool_recycle=10, pool_size=10, max_overflow=20)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
Base = declarative_base()


# データベースに接続するための処理
def db_session():
    return scoped_session(sessionmaker(autocommit=False, autoflush=False, bind=engine))

8. models.pyファイルを以下の内容にする

models.py
from database.database import Base
from datetime import datetime
from sqlalchemy import Column, VARCHAR, INT, TEXT, DATETIME
import pytz

jst = pytz.timezone('Asia/Tokyo')


# ユーザーのモデルを用意
class User(Base):
    __tablename__ = 'users'
    __table_args__ = {"mysql_collate": "utf8_general_ci"}
    id = Column(INT, primary_key=True, autoincrement=True, unique=True, nullable=False, index=True)
    name = Column(VARCHAR(255), primary_key=False, autoincrement=False, unique=False, nullable=False, index=False)
    email = Column(VARCHAR(255), primary_key=False, autoincrement=False,  unique=True, nullable=False, index=True)
    password = Column(TEXT, primary_key=False, autoincrement=False, unique=False, nullable=False, index=False)
    create_time = Column(DATETIME, primary_key=False, autoincrement=False, unique=False, nullable=False, index=False, default=lambda: datetime.now(jst)) 

9. ターミナルにてコマンドを入力してマイグレーションフォルダを生成する

ターミナル.
alembic init migrations

10. プロジェクト直下にmigrations / alembic.iniが生成される

フォルダ構成.
.
├── main.py
├── env.py
├── .env
├── requirements.txt
├── alembic.ini
├── database
│   ├── database.py
│   └── models.py
└── migrations
    ├── README
    ├── env.py
    ├── script.py.mako
    └── versions

11. migrations/env.pyファイルを以下の内容にする

env.py
from sqlalchemy import pool
from sqlalchemy import engine_from_config
from alembic import context
from database import database, models
from logging.config import fileConfig

config = context.config

if config.config_file_name is not None:
    fileConfig(config.config_file_name)

# target_metadata = database.Base.metadata # どちらかを使用する
target_metadata = models.Base.metadata     # どちらかを使用する

config.set_main_option("sqlalchemy.url", str(database.database_url))


def run_migrations_offline() -> None:

    url = config.get_main_option("sqlalchemy.url")
    context.configure(
        url=url,
        target_metadata=target_metadata,
        literal_binds=True,
        dialect_opts={"paramstyle": "named"},
    )

    with context.begin_transaction():
        context.run_migrations()


def run_migrations_online() -> None:

    connectable = engine_from_config(
        config.get_section(config.config_ini_section),
        prefix="sqlalchemy.",
        poolclass=pool.NullPool,
    )

    with connectable.connect() as connection:
        context.configure(
            connection=connection, target_metadata=target_metadata
        )

        with context.begin_transaction():
            context.run_migrations()


if context.is_offline_mode():
    run_migrations_offline()
else:
    run_migrations_online() 

12. main.pyファイルを以下の内容にする

main.py
import subprocess

if __name__ == "__main__":
    print("データベースのマイグレーションを開始します")

    print("新規マイグレーションスクリプトを生成します")
    cmd = "alembic revision --autogenerate"
    subprocess.call(cmd.split(" "))
    print("マイグレーションスクリプトの生成が終了しました")

    print("データベースのスキーマを最新の状態に更新します")
    cmd = "alembic upgrade head"
    subprocess.call(cmd.split(" "))
    print("データベースのスキーマを最新の状態に更新しました")

    print("データベースのマイグレーションが完了しました")

13. ターミナルにてコマンドを入力してマイグレーションを実行する

ターミナル.
python main.py

14. migrations/versions直下にマイグレーションスクリプトが生成される

フォルダ構成.
.
├── main.py
├── migration_database.py
├── env.py
├── .env
├── requirements.txt
├── alembic.ini
├── database
│   ├── database.py
│   └── models.py
└── migrations
    ├── README
    ├── env.py
    ├── script.py.mako
    └── versions
        └── マイグレーションスクリプト
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0