SQLAlchemy

sqlalchemy의 declarative_base()를 fastapi에서 사용하는 이유

bluebamus 2025. 2. 8.

1. declarative_base()를 사용하는 이유

   - SQLAlchemy에는 크게 두 가지 방식의 ORM이 있다.

      1. Declarative ORM 방식 (클래스를 테이블처럼 사용)

      2. Imperative (Core) 방식 (SQLAlchemy의 Table 객체를 직접 사용)

 

   - declarative_base()를 사용하면 클래스를 통해 테이블을 정의할 수 있으며, Pythonic한 방식으로 ORM을 사용할 수 있다. 이를 사용하면 다음과 같은 장점이 있다:

      ✅ 객체 지향적인 코드 작성 → Python 클래스를 테이블처럼 활용

      ✅ 자동 테이블 생성 및 관리 → Base.metadata.create_all(engine)을 통해 테이블 자동 생성

      ✅ 관계(Relationship) 표현 가능 → relationship을 사용하여 쉽게 테이블 간 관계 설정 가능

      ✅ 코드 가독성 증가 → 클래스 기반의 테이블 정의 방식으로 직관적인 코드 작성 가능

      ✅ Pydantic과의 호환성 → FastAPI에서 Pydantic 모델과 쉽게 연동 가능

 

2. declarative_base() 기본 사용법

   - declarative_base()를 사용하면 테이블을 Python 클래스로 정의할 수 있다.

 

   - 기본적인 테이블 정의

      - Base = declarative_base() → Base를 상속받아 모든 모델을 정의
      - __tablename__ → 데이터베이스 테이블 이름 설정
      - Column을 이용하여 테이블 컬럼 정의

from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.orm import declarative_base, sessionmaker

Base = declarative_base()  # 모든 ORM 모델의 부모 클래스

class User(Base):
    __tablename__ = "users"  # 테이블 이름 지정
    id = Column(Integer, primary_key=True, index=True)  # 기본키
    name = Column(String, nullable=False)  # 문자열 컬럼
    age = Column(Integer)  # 정수 컬럼

# 데이터베이스 연결
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL, echo=True)
Base.metadata.create_all(bind=engine)  # 테이블 자동 생성

 

3. declarative_base()에서 활용 가능한 주요 기능

   1) __tablename__을 이용한 테이블 이름 설정

      - 각 모델 클래스에서 __tablename__을 정의하여 데이터베이스 테이블 이름을 직접 설정할 수 있다.

class Product(Base):
    __tablename__ = "products"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, nullable=False)

 

   2) Column을 이용한 다양한 데이터 타입 설정

      - Column을 사용하면 SQLAlchemy에서 제공하는 여러 데이터 타입을 사용할 수 있다.

from sqlalchemy import Boolean, DateTime, Float, Text
from datetime import datetime

class Order(Base):
    __tablename__ = "orders"
    id = Column(Integer, primary_key=True, index=True)
    price = Column(Float, nullable=False)  # 실수 타입
    description = Column(Text)  # 긴 문자열 저장
    is_paid = Column(Boolean, default=False)  # Boolean 타입
    created_at = Column(DateTime, default=datetime.utcnow)  # 생성 시간

 

   3) relationship을 이용한 테이블 간 관계 설정

      - SQLAlchemy ORM에서 relationship을 사용하면 1:1, 1:N, N:M 관계를 쉽게 설정할 수 있다.

 

      1. 1:N 관계 (One-to-Many)

from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship

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

    # 1:N 관계 설정 (User -> Post)
    posts = relationship("Post", back_populates="owner")

class Post(Base):
    __tablename__ = "posts"
    id = Column(Integer, primary_key=True, index=True)
    title = Column(String, nullable=False)
    user_id = Column(Integer, ForeignKey("users.id"))  # 외래 키 설정

    # 역방향 관계
    owner = relationship("User", back_populates="posts")

 

4. __mapper_args__의 다양한 활용 예제

   1) 다형성 설정 (객체 타입에 따라 다르게 매핑)

      - polymorphic_on: 해당 컬럼의 값을 기준으로 상속된 클래스를 구분
      - polymorphic_identity: 이 클래스의 기본 유형을 설정

class Employee(Base):
    __tablename__ = "employees"
    id = Column(Integer, primary_key=True, index=True)
    type = Column(String, nullable=False)

    __mapper_args__ = {
        "polymorphic_identity": "employee",  # 기본 유형을 'employee'로 설정
        "polymorphic_on": type  # 다형성을 적용할 기준 컬럼
    }

 

   2) Versioning (버전 관리) 설정 (데이터 변경 시 버전 번호 증가)

      - version_id_col: 데이터 변경 시 자동으로 증가하는 버전 관리 컬럼

class Document(Base):
    __tablename__ = "documents"
    id = Column(Integer, primary_key=True, index=True)
    version = Column(Integer, nullable=False, default=1)

    __mapper_args__ = {
        "version_id_col": version  # 변경 시 자동 증가할 버전 컬럼
    }

 

   3) 테이블 자동 로드 설정 (기존 DB 테이블을 자동으로 로드)

class AutoLoadTable(Base):
    __tablename__ = "autoload_table"
    __mapper_args__ = {
        "autoload": True  # 테이블 스키마 자동 로드
    }

 

   4) 기본 정렬 기준 설정 (쿼리 결과 기본 정렬)

class Product(Base):
    __tablename__ = "products"
    id = Column(Integer, primary_key=True, index=True)
    name = Column(String, nullable=False)

    __mapper_args__ = {
        "order_by": name  # 기본 정렬을 'name' 컬럼 기준으로 설정
    }

 

   5) Abstract Base Class 설정 (공통 속성을 가지는 기본 클래스 정의)

      - __abstract__ = True: 이 클래스를 직접 테이블로 생성하지 않고 상속 전용으로 사용

class BaseModel(Base):
    __abstract__ = True  # 이 클래스 자체는 테이블을 생성하지 않음
    id = Column(Integer, primary_key=True, index=True)

class User(BaseModel):
    __tablename__ = "users"
    name = Column(String, nullable=False)

 

댓글