FastAPI - SQLModel에 Alembic 모듈 적용하기

헨도·2024년 1월 24일
0

FastAPI

목록 보기
8/8
post-thumbnail

Alembic

  • 웹 서비스를 개발하고 배포한 뒤에 반드시 하는 업데이트는 데이터베이스 마이그레이션입니다.
  • 데이터베이스를 마이그레이션하는 경우는 보통 아래 3가지가 대표적입니다.
	1. 인스턴스의 이전
	   - 클라우드를 사용한다면 다른 인스턴스 혹은 다른 리전으로 이전해야할 경우
       
    2. 특정 Entity에 대한 변경 요청
       - 기능을 새로 넣기위해 추가 컬럼이 필요할 경우
       
    3. 일부 영속된 데이터를 특정 데이터로 변경
  • Alembic은 SQLAlchemy 기반의 마이그레이션 도구로 Python을 사용하지만, 별도로 사용할 수 있어 Python 또는 SQLAlchemy에 사용할 필요는 없습니다.
  • 모든 마이그레이션은 Alembic 스크립트에 의해 이루어집니다.
  • 버전 관리를 위해 각 인스턴스에서는 migration 테이블이 존재하고, 이 테이블을 통해 마이그레이션 진행도를 확인할 수 있으며 이를 통해 중복 마이그레이션 및 버전 관리를 할 수 잇습니다.

Alembic 설치

pip install alembic
  • 설치 명령어이며, 설치 후에는 커맨드 라인에서 alembic 명령어를 사용할 수 있습니다.

Alembic 모듈 초기화

alembic init [디렉터리 이름]

ex) alembic init migrations

초기화 이후 디렉터리 이름으로 폴더가 만들어지며, 그 폴더안에 Alembic 과 관련된 파일이 생성됩니다.

alembic.ini 파일은 폴더 밖 경로에 만들어집니다.

  • 파일 설명
    • alembic.ini
      • env.py 파일에서 Configuration 파일로 사용되는 Alembic 설정 파일
    • env.py
      • 마이그레이션 시 실행되는 서버 연결 및 마이그레이션 실행 코드
    • script.py.mako
      • 마이그레이션 스크립트 템플릿 파일
    • versions
      • 이 폴더에 마이그레이션 할 스크립트 코드가 저장

DB 설정

  • 마이그레이션에 필요한 파일들을 생성했으니 DB를 연동하기 위해 세팅을 해야한다.
    alembic.ini 파일에 들어가 sqlalchemy.url를 찾아서 수정한다.
sqlalchemy.url = sqlite:///[DB 명].db

주의 !

  • ini 파일이기 때문에 python에서 제공하는 String 관련 함수를 사용할 수 없으며 오직 정적 문자열만 이용해야한다.
    그런데 이렇게 설정하면 환경별로 마이그레이션을 할 때마다 인스턴스 주소를 바꿔줘야하는 불편함이 생긴다.

    이런 경우, alembic.ini 파일에 있는 sqlalchemy.url 변수를 제거한 후 env.py 파일에서 sqlalchemy.url을 수동으로 설정할 수 있는 방법이 있다.

env.py

config = context.config

if not config.get_main_option("sqlalchemy.url") :
	config.set_main_option("sqlalchemy.url", DB 정보)
    )
    
- ex) MySQL 적용

config = context.config

if not config.get_main_option("sqlalchemy.url") :
	config.set_main_option("sqlalchemy.url", 
    		"mysql+pymysql://{username}:{password}@{host}:{port}/{db_name}".format(
            username="root",
            password="1234",
            host="localhost",
            port="3306",
            db_name="test_db"
    )
    
 - ex) sqlite 적용
 
config = context.config

if not config.get_main_option("sqlalchemy.url") :
	config.set_main_option("sqlalchemy.url", sqlite://blacklist.db)
  • env.py 파일에는 alembic에서 제공하는 context가 있고, 이 context를 통해서 DB 인스턴스 주소를 결정할 수 있다.
  • env.py 파일은 python 코드이기 때문에 python에서 제공하는 함수 사용 가능

SQLModel 환경 설정

  • SQLAlchemy가 아닌 SQLModel을 사용하기 위해선 몇가지 사항들을 추가해야한다.
  1. script.py.mako 파일 수정
script.py.mako

"""${message}

Revision ID: ${up_revision}
Revises: ${down_revision | comma,n}
Create Date: ${create_date}

"""
from typing import Sequence, Union

from alembic import op
import sqlalchemy as sa

import sqlmodel <--------------- 구문 추가 --------------->

${imports if imports else ""}

# revision identifiers, used by Alembic.
revision: str = ${repr(up_revision)}
down_revision: Union[str, None] = ${repr(down_revision)}
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}


def upgrade() -> None:
    ${upgrades if upgrades else "pass"}


def downgrade() -> None:
    ${downgrades if downgrades else "pass"}
  1. env.py 파일 수정
from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool

from alembic import context

from sqlmodel import SQLModel <--------------- 구문 추가 --------------->

# this is the Alembic Config object, which provides
# access to the values within the .ini file in use.
config = context.config

# Interpret the config file for Python logging.
# This line sets up loggers basically.
if config.config_file_name is not None:
    fileConfig(config.config_file_name)

# add your model's MetaData object here
# for 'autogenerate' support
# from myapp import mymodel
# target_metadata = mymodel.Base.metadata
# Base = declarative_base()
target_metadata = SQLModel.metadata <--------------- 구문 추가 --------------->

# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.


def run_migrations_offline() -> None:
    """Run migrations in 'offline' mode.

    This configures the context with just a URL
    and not an Engine, though an Engine is acceptable
    here as well.  By skipping the Engine creation
    we don't even need a DBAPI to be available.

    Calls to context.execute() here emit the given string to the
    script output.

    """
    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:
    """Run migrations in 'online' mode.

    In this scenario we need to create an Engine
    and associate a connection with the context.

    """
    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()

마이그레이션 스크립트 생성

  • Alembic 에서 마이그레이션 스크립트 템플릿을 자동으로 만들어주는 명령어를 사용하여 초기 파일 생성
alembic revision -m "commit 메시지 작성"

ex)
alembic revision -m "first alembic"
  • revision 명령어 뒤, m 옵션을 사용하여 메모를 적을 수 있다.

마이그레이션 자동 스크립트 생성

  • Alembic 에서 제공하는 autogenerate 옵션은 SQLModel의 ORM을 이용하여 정의한 Model의 메타데이터를 읽어 변경된 부분을 자동으로 확인하여 마이그레이션 스크립트를 작성해주는 옵션입니다.
  • 하지만 단점은 스크립트 작성이 시작된 시작부터 꾸준히 사용해야하며 중간에 사용할 경우 변경점을 찾지 못하여 마이그레이션이 꼬일 수 있습니다.
alembic revision --autogenerate -m "first alembic"

명령어 수행 시, 자동으로 version 폴더 안에 마이그레이션 코드가 만들어집니다.

그리고 내부 upgrade와 downgrade에 필요한 DB 정보의 추가, 수정 내역이 있는지 확인하시고, 잘 작동하시면 Alembic 적용이 성공적으로 된 것입니다.

profile
Junior Backend Developer

0개의 댓글