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


alembic.ini에서 sql_url을 설정해도 되지만 나는 미리 만들어준 변수를 활용할거기 때문에 alembic폴더 안에 있는 env.py에서 url설정을 해줄거다.
환경변수를 변수로 사용할 수 있게 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()
위에서 정의한 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,
)
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할 수 있다고...(나는 이거 안해서 개고생했다ㅠㅠ)
기본적인 명령어!
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 공식사이트