Study/fastapi

FastAPI에서 SQLAlchemy를 사용하여 테이블을 생성하는 방법

bluebamus 2025. 2. 17.

1. Base.metadata.create_all(bind=engine)

   1) 설명:

      - SQLAlchemy의 create_all() 메서드는 Base 클래스를 상속받은 모든 모델에 대해 테이블을 생성한다.
      - 일반적으로 FastAPI에서 engine을 정의하고 Base.metadata.create_all(bind=engine)을 호출하면 한 번에 여러 개의 테이블을 생성할 수 있다.

      - 이미 존재하는 테이블은 무시되며, 존재하지 않는 테이블만 생성한다.

 

   2) 사용법:

from sqlalchemy import create_engine
from sqlalchemy.orm import declarative_base

DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL)

Base = declarative_base()

# 테이블 정의
from sqlalchemy import Column, Integer, String

class User(Base):
    __tablename__ = "users"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, index=True)

# 테이블 생성
Base.metadata.create_all(bind=engine)

 

   3) 특징:

      - Base 클래스를 기반으로 모든 테이블을 한 번에 생성함
      - 존재하는 테이블은 무시됨
      - 초기 스키마 설정 시 사용

 

2. User.__table__.create(bind=engine)

   1) 설명:

      - 특정 테이블만 개별적으로 생성할 때 사용한다.
      - Base.metadata.create_all()과 달리, 하나의 테이블만 생성할 수 있다.
      - 테이블이 이미 존재하면 예외 발생한다. (sqlalchemy.exc.OperationalError)

 

   2) 사용법:

# 특정 테이블만 생성
User.__table__.create(bind=engine)

 

   3) 특징:

      - 단일 테이블만 생성 가능

      - 존재하는 테이블이면 예외 발생

      - 특정 테이블만 개별적으로 생성할 때 유용

 

3. Base.metadata.tables['users'].create(bind=engine)

   1) 설명:

      - 특정 테이블을 Base.metadata.tables에서 가져와 생성할 수 있다.

      - User.__table__.create(bind=engine)과 비슷하지만, 테이블명을 문자열로 접근할 수 있다.

 

   2) 사용법:

# 특정 테이블 생성
Base.metadata.tables['users'].create(bind=engine)

 

   3) 특징:

      - 테이블 이름을 문자열로 다룰 수 있어 동적 테이블 생성이 가능
      - 특정 테이블만 선택적으로 생성 가능

 

4. engine.execute(DDL("CREATE TABLE ..."))

   1) 설명:

      - DDL()을 사용하여 직접 SQL 쿼리를 실행할 수도 있다.
      - 특정 테이블이 존재하는지 확인하고, 없으면 생성하는 방식으로 활용할 수 있다.

 

   2) 사용법:

from sqlalchemy import text
from sqlalchemy.sql import DDL

# SQL 쿼리 직접 실행
engine.execute(DDL("""
    CREATE TABLE IF NOT EXISTS users (
        id INTEGER PRIMARY KEY AUTOINCREMENT,
        name TEXT
    )
"""))

 

   3) 특징:

      - SQL 쿼리를 직접 실행하므로, 더 세밀한 제어 가능
      - 테이블 존재 여부를 직접 확인할 수 있음
      - DBMS에 따라 다르게 작성해야 함 (MySQL, PostgreSQL 등)

 

5. Alembic을 사용한 마이그레이션 기반 생성

   1) 설명:

      - create_all()을 사용하면 기존 테이블을 변경할 수 없음 (마이그레이션 기능 없음)
      - Alembic을 사용하면 버전 관리를 하면서 테이블을 변경하거나 추가할 수 있음

 

   2) 사용법:

      1.Alembic 초기화

alembic init alembic

 

      2.alembic/versions/ 폴더에 마이그레이션 스크립트 생성

alembic revision --autogenerate -m "create users table"

 

      3. 생성된 스크립트(alembic/versions/xxxx_create_users_table.py)를 수정한 후 적용

alembic upgrade head

 

   3) 특징:

      - create_all()과 달리 테이블 변경(ALTER TABLE) 가능
      - 버전 관리가 가능하여 실무에서 필수적으로 사용됨

 

6. if not engine.dialect.has_table(engine, "users")

   1) 설명:

      - engine.dialect.has_table()을 사용하여 테이블이 존재하는지 확인한 후 생성할 수 있다.
      - Base.metadata.create_all()을 호출하기 전에 특정 테이블이 있는지 검사할 때 유용하다.

 

   2) 사용법:

from sqlalchemy.engine.reflection import Inspector

# 엔진을 통해 테이블 존재 여부 확인
if not engine.dialect.has_table(engine, "users"):
    User.__table__.create(bind=engine)

 

   3) 특징:

      - 테이블 존재 여부를 직접 확인 가능
      - 특정 테이블이 존재할 경우 중복 생성 방지 가능

 

7. 비교 정리

방법 특징 단일/전체 테이블 테이블 존재 시 동작
Base.metadata.create_all(bind=engine) 모든 테이블 한 번에 생성 전체 테이블 존재하는 테이블 무시
User.__table__.create(bind=engine) 특정 테이블만 생성 단일 테이블 존재 시 예외 발생
Base.metadata.tables['users'].create(bind=engine) 특정 테이블만 생성 (문자열 접근) 단일 테이블 존재 시 예외 발생
engine.execute(DDL("CREATE TABLE ...")) 직접 SQL 실행 가능 단일 테이블 (직접 정의) IF NOT EXISTS로 제어 가능
Alembic 마이그레이션 버전 관리 가능, 테이블 변경 가능 전체/단일 테이블 마이그레이션 방식 적용
engine.dialect.has_table(engine, "users") 테이블 존재 여부 확인 후 생성 단일 테이블 존재 시 생성 안 함

 

8. 어떤 방법을 사용할까?

   1) 초기 개발 단계

      - Base.metadata.create_all(bind=engine) → 전체 테이블 생성
      - User.__table__.create(bind=engine) → 개별 테이블만 필요할 때

 

   2) 운영 및 유지보수 단계

      - Alembic 마이그레이션 필수 (테이블 변경 관리 필요)
      - 직접 SQL(engine.execute(DDL(...)))을 실행해야 하는 경우도 있음

 

   3) 특정 테이블만 생성해야 할 때

      - Base.metadata.tables['users'].create(bind=engine) 또는 User.__table__.create(bind=engine)
      - 존재 여부 확인하려면 engine.dialect.has_table(engine, "users") 활용

댓글