필드 (Fields)

필드 (Fields)

원문: https://sqlmodel.tiangolo.com/tutorial/fields/ (해당 URL은 현재 404 — 공식 튜토리얼의 "필드/컬럼 정의" 내용 기반 번역)

SQLModel에서 필드(Field) 는 테이블 모델 클래스 안의 각 변수(속성)로, SQL 데이터베이스 테이블의 컬럼(Column) 하나에 해당합니다.

필드와 컬럼 정의하기

테이블 모델 클래스를 만들었다면, 이제 표준 파이썬 타입 어노테이션(type annotations)을 사용해 클래스의 필드 또는 컬럼 을 정의할 차례입니다.

각 변수의 이름이 테이블의 컬럼 이름이 되고, 각 변수의 타입이 테이블 컬럼의 타입이 됩니다:

from typing import Optional
from sqlmodel import Field, SQLModel


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str
    secret_name: str
    age: Optional[int] = None

None 필드, Nullable 컬럼

age 필드를 봅시다. 그 타입이 int | None이라는 점에 주목하세요.

이것은 파이썬에서 어떤 값이 "int일 수도 있고 None일 수도 있다"는 것을 선언하는 표준 방식입니다.

그리고 age기본값None으로 설정했습니다:

age: Optional[int] = None

/// tip

idint | None으로 정의합니다. 하지만 id에 대해서는 아래에서 다시 설명합니다.

///

타입이 int | None이기 때문에:

  • 데이터를 검증(validate) 할 때, age에는 None이 허용되는 값이 됩니다.
  • 데이터베이스에서 age 컬럼은 NULL(파이썬의 None에 해당하는 SQL 값)을 허용하게 됩니다.

그리고 기본값이 = None이기 때문에:

  • 데이터를 검증할 때, 이 age 필드는 필수(required) 가 아니며 기본적으로 None입니다.
  • 데이터베이스에 저장할 때, age 컬럼의 기본값은 NULL입니다.

/// tip

기본값은 다른 값(예: = 42)일 수도 있습니다.

///

기본 키(Primary Key) id

이제 id 필드를 살펴봅시다. 이것은 테이블의 기본 키(primary key) 입니다. 기본 키란 특정 테이블의 각 행을 식별하는 고유한 식별자입니다.

따라서 id기본 키로 표시해야 합니다.

이를 위해 sqlmodel에서 가져온 특수한 Field 함수를 사용하고, primary_key=True 인자를 설정합니다:

id: Optional[int] = Field(default=None, primary_key=True)

이렇게 하면 SQLModel에게 이 id 필드/컬럼이 테이블의 기본 키임을 알려줍니다.

그런데 SQL 데이터베이스 내부에서 기본 키는 항상 필수이고 NULL이 될 수 없습니다. 그렇다면 왜 int | None으로 선언할까요?

id는 데이터베이스에서 필수이지만, 우리 코드가 아니라 데이터베이스가 생성하기 때문입니다.

따라서 이 클래스의 인스턴스를 만들 때(다음 장에서) 우리는 id설정하지 않을 것입니다. 그리고 id의 값은 데이터베이스에 저장하기 전까지는 None이고, 저장한 뒤에야 마침내 값을 가지게 됩니다:

my_hero = Hero(name="Spider-Boy", secret_name="Pedro Parqueador")

do_something(my_hero.id)  # Oh no! my_hero.id is None! 😱🚨

# 아래는 데이터베이스에 저장한다고 상상해보세요
somehow_save_in_db(my_hero)

do_something(my_hero.id)  # 이제 my_hero.id는 DB에서 생성된 값을 가집니다 🎉

우리 코드(데이터베이스가 아닌)에서는 id의 값이 None수도 있으므로 int | None을 사용합니다. 이렇게 하면 에디터가 도와줄 수 있습니다. 예를 들어 아직 데이터베이스에 저장하지 않아 None인 객체의 id에 접근하려 할 때 에디터가 경고를 표시해 줍니다.

이제 우리는 기본값의 자리를 Field() 함수로 대체했기 때문에, Field()default=None 인자로 id실제 기본값None으로 설정합니다:

Field(default=None)

만약 default 값을 설정하지 않았다면, 이후에 이 모델로 데이터 검증(Pydantic 기반)을 수행할 때 int 외에 None 값도 허용하겠지만, 여전히 그 None 값을 전달하도록 요구하게 됩니다. 이는 나중에 이 모델을 사용하는 사람(아마 우리 자신)을 혼란스럽게 하므로, 여기서 기본값을 설정하는 것이 좋습니다.

Field()의 주요 인자

Field()는 기본 키 설정 외에도 컬럼의 다양한 속성을 지정할 수 있습니다. 기술적으로는 Pydantic의 필드 제약(Pydantic constraints)과 SQLAlchemy의 컬럼 인자(column arguments)를 하나의 호출에서 함께 받습니다.

from sqlmodel import Field, SQLModel


class Hero(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    name: str = Field(index=True)
    secret_name: str
    age: Optional[int] = Field(default=None, index=True)
    team_id: Optional[int] = Field(default=None, foreign_key="team.id")

자주 쓰이는 Field() 인자는 다음과 같습니다:

  • default: 필드의 기본값을 지정합니다. None으로 두면 데이터베이스에서 NULL 기본값을 의미합니다.
  • primary_key: True로 설정하면 이 컬럼을 테이블의 기본 키로 지정합니다.
  • index: True로 설정하면 이 컬럼에 데이터베이스 인덱스(index) 를 만듭니다. 쿼리 성능을 최적화할 때 유용합니다.
  • foreign_key: 다른 테이블의 컬럼(예: "team.id")을 참조하는 외래 키를 지정합니다. 테이블을 연결(JOIN)할 때 사용합니다.
  • unique: True로 설정하면 이 컬럼의 값이 테이블에서 고유해야 합니다.
  • sa_column: SQLModel의 Field()가 표면화하지 못하는 고급 설정(예: 서버 기본값, 커스텀 컬럼 타입, 복합 제약 조건)이 필요할 때, 완전한 SQLAlchemy Column을 전달할 수 있는 탈출구(escape hatch)입니다.

모델의 종류: 테이블 모델 vs 데이터 모델

table=True 설정으로 SQLModel에 이것이 테이블 모델임을 알려줍니다. 즉 데이터베이스의 테이블을 나타낸다는 뜻입니다.

반면 table=True가 없는 모델은 데이터 모델(data model) 로, 데이터베이스에 테이블이 없는 순수한 검증/직렬화 모델입니다. 이런 데이터 모델은 나중에 매우 유용하지만, 지금은 우선 table=True 설정만 계속 추가해 나가겠습니다.

/// note

SQLModel 모델 클래스는 동시에 Pydantic 모델이기도 합니다(내부적으로 실제로 Pydantic 모델입니다). 따라서 Field()는 검증/직렬화를 위한 Pydantic 필드 제약과, DDL 및 쿼리를 위한 SQLAlchemy 컬럼 인자를 한 번에 모두 해석합니다.

///