[alembic] alembic 시작하기

서경원·2024년 1월 31일
post-thumbnail

1. alembic 을 pip 로 설치

pip install alembic

2. alembic 초기화

alembic이란 이름으로 alembic을 초기화 해준다. -> alembic이란 이름의 폴더와 초기 파일들이 생성된다.
alembic.ini은 별도로 프로젝트의 루트경로에 생성된다!!

alembic init alembic


3. 초기 설정하기

alembic.ini에서 sql_url을 설정해도 되지만 나는 미리 만들어준 변수를 활용할거기 때문에 alembic폴더 안에 있는 env.py에서 url설정을 해줄거다.

config.py

환경변수를 변수로 사용할 수 있게 Settings를 정의해준다.

from pydantic_settings import BaseSettings
from functools import lru_cache

class Settings(BaseSettings):
    # MySQL 설정 정보
    MYSQL_HOST: str
    MYSQL_PORT: int
    MYSQL_ROOT_PASSWORD: str
    MYSQL_DATABASE: str
    MYSQL_USER: str
    MYSQL_PASSWORD: str
    
    class Config:
    env_file = ".env"

@lru_cache()
def get_setting():
    return Settings()

session.py

위에서 정의한 Setting을 통해 환경변수로 SQLALCHEMY_DATABASE_URL를 만든다.

from ..core.config import get_setting
from sqlalchemy import text
from sqlalchemy.orm import DeclarativeBase

from typing import Generator

settings = get_setting()

SQLALCHEMY_DATABASE_URL = "mysql+pymysql://{}:{}@{}:{}/{}".format(
    settings.MYSQL_USER,
    settings.MYSQL_PASSWORD,
    settings.MYSQL_HOST,
    settings.MYSQL_PORT,
    settings.MYSQL_DATABASE,
)

env.py

alembic 폴더 내의 env.py 파일을 수정한다!!

config.set_main_option()함수를 활용해서 sqlalchemy의 url을 위에서 만든 SQLALCHEMY_DATABASE_URL로 설정해준다.

from app.db.session import SQLALCHEMY_DATABASE_URL

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config
config.set_main_option('sqlalchemy.url', SQLALCHEMY_DATABASE_URL)

url 설정을 마쳤으면 Base의 metadata를 지정해줘야한다. (그래야 alembic이 model들을 인식해서 autogenerate했을 때 수정사항에 따라 revision을 잘 만들어 준다!!)

from app.db.session import Base

from app.models.v1.exercise import Exercise
from app.models.v1.menu import Menu
from app.models.v1.user import User
from app.models.v1.userDietPlanInfo import UserDietPlanInfo
target_metadata = Base.metadata

여기서 핵심은 모든 모델들을 import 해줘야 한다는 것이다!!!! 그래야지 Base가 모든 model들을 populate할 수 있다고...(나는 이거 안해서 개고생했다ㅠㅠ)

4. revision 생성하기!!

기본적인 명령어!

alembic revision -m "{원하는 이름}"

이렇게 명령어를 입려했을 때 upgrade와 downgrade가 아무것도 없는 상태로 revision이 생성된다.

"""{원하는 이름}

Revision ID: a2d4e724e2bc
Revises: 8e8cb463a2de
Create Date: 2024-01-31 18:18:16.562681

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = 'a2d4e724e2bc'
down_revision: Union[str, None] = '8e8cb463a2de'
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    pass


def downgrade() -> None:
    pass

autogenerate!!

alembic revision --autogenerate -m "{원하는 이름}"

이렇게 생성할 경우 현재 내 모델에서 변경사항이 있는 경우 반영하여 revision 파일이 생성된다!

"""First revision

Revision ID: 8e8cb463a2de
Revises: 
Create Date: 2024-01-31 17:43:23.331619

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision: str = '8e8cb463a2de'
down_revision: Union[str, None] = None
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('user',
    sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
    sa.Column('kakao_id', sa.String(length=255), nullable=False),
    sa.Column('name', sa.String(length=255), nullable=True),
    sa.Column('height', sa.Float(), nullable=True),
    sa.Column('weight', sa.Float(), nullable=True),
    sa.Column('age', sa.Integer(), nullable=True),
    sa.Column('gender', sa.String(length=255), nullable=True),
    sa.Column('target_weight', sa.Float(), nullable=True),
    sa.Column('kakao_access_token', sa.String(length=255), nullable=False),
    sa.Column('kakao_refresh_token', sa.String(length=255), nullable=False),
    sa.Column('jwt_refresh_token', sa.String(length=255), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('user_diet_plan_info',
    sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='Auto Increment'),
    sa.Column('user_id', sa.Integer(), nullable=False, comment='Auto Increment'),
    sa.ForeignKeyConstraint(['user_id'], ['user.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('exercise',
    sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='Auto Increment'),
    sa.Column('user_diet_plan_info_id', sa.Integer(), nullable=True, comment='Auto Increment'),
    sa.Column('advice', sa.Text(), nullable=True),
    sa.Column('recommended_exercise', sa.Text(), nullable=True),
    sa.Column('excess_calories', sa.Float(), nullable=True),
    sa.ForeignKeyConstraint(['user_diet_plan_info_id'], ['user_diet_plan_info.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('menu',
    sa.Column('id', sa.Integer(), autoincrement=True, nullable=False, comment='Auto Increment'),
    sa.Column('user_diet_plan_info_id', sa.Integer(), nullable=False, comment='Auto Increment'),
    sa.Column('name', sa.String(length=255), nullable=True),
    sa.Column('calories', sa.Float(), nullable=True),
    sa.Column('meal_time', sa.String(length=255), nullable=True),
    sa.Column('created_at', sa.TIMESTAMP(), nullable=True),
    sa.ForeignKeyConstraint(['user_diet_plan_info_id'], ['user_diet_plan_info.id'], ),
    sa.PrimaryKeyConstraint('id')
    )
    # ### end Alembic commands ###


def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('menu')
    op.drop_table('exercise')
    op.drop_table('user_diet_plan_info')
    op.drop_table('user')
    # ### end Alembic commands ###

위에서 env.py 설명할 때 metadata 설정시 모든 모델들을 import 하지 않으면 autogenerate를 해도 기본적인 revision 처럼 upgrade와 downgrade가 모두 pass만 나오거나 이상한 코드들이 나올 수 있다.

reference
alembic autogenerate revision try to drop all the index and tables of my database
alembic 공식사이트

profile
멋진 사나이

0개의 댓글