o2o-castad-backend/docs/reference/ConfigDict.md

11 KiB

Pydantic ConfigDict 사용 매뉴얼

개요

Pydantic v2에서 ConfigDict는 모델의 유효성 검사, 직렬화, JSON 스키마 생성 등의 동작을 제어하는 설정을 정의하는 TypedDict입니다.

Pydantic v1의 class Config는 더 이상 권장되지 않으며, ConfigDict를 사용해야 합니다.

기본 사용법

from pydantic import BaseModel, ConfigDict

class MyModel(BaseModel):
    model_config = ConfigDict(
        str_strip_whitespace=True,
        strict=True
    )

    name: str
    age: int

설정 옵션 전체 목록

문자열 처리

옵션 타입 기본값 설명
str_to_lower bool False 문자열을 소문자로 변환
str_to_upper bool False 문자열을 대문자로 변환
str_strip_whitespace bool False 문자열 앞뒤 공백 제거
str_min_length int | None None 문자열 최소 길이
str_max_length int | None None 문자열 최대 길이

예시:

class UserInput(BaseModel):
    model_config = ConfigDict(
        str_strip_whitespace=True,
        str_to_lower=True,
        str_min_length=1,
        str_max_length=100
    )

    username: str

user = UserInput(username="  HELLO  ")
print(user.username)  # "hello"

유효성 검사

옵션 타입 기본값 설명
strict bool False 엄격한 타입 검사 활성화 (타입 강제 변환 비활성화)
validate_assignment bool False 속성 할당 시 유효성 검사 수행
validate_default bool False 기본값도 유효성 검사 수행
validate_return bool False 반환값 유효성 검사
revalidate_instances Literal['always', 'never', 'subclass-instances'] 'never' 모델 인스턴스 재검증 시점
arbitrary_types_allowed bool False Pydantic이 지원하지 않는 타입 허용

예시 - strict 모드:

class StrictModel(BaseModel):
    model_config = ConfigDict(strict=True)

    count: int

# strict=False (기본값): "123" -> 123 자동 변환
# strict=True: "123" 입력 시 ValidationError 발생

예시 - validate_assignment:

class User(BaseModel):
    model_config = ConfigDict(validate_assignment=True)

    age: int

user = User(age=25)
user.age = "invalid"  # ValidationError 발생

Extra 필드 처리

옵션 타입 기본값 설명
extra 'allow' | 'ignore' | 'forbid' 'ignore' 추가 필드 처리 방식

값 설명:

  • 'ignore': 추가 필드 무시 (기본값)
  • 'allow': 추가 필드 허용, __pydantic_extra__에 저장
  • 'forbid': 추가 필드 입력 시 에러 발생

예시:

class AllowExtra(BaseModel):
    model_config = ConfigDict(extra='allow')

    name: str

data = AllowExtra(name="John", unknown_field="value")
print(data.__pydantic_extra__)  # {'unknown_field': 'value'}

class ForbidExtra(BaseModel):
    model_config = ConfigDict(extra='forbid')

    name: str

ForbidExtra(name="John", unknown="value")  # ValidationError 발생

불변성 (Immutability)

옵션 타입 기본값 설명
frozen bool False 모델을 불변(immutable)으로 만듦, __hash__() 구현

예시:

class ImmutableUser(BaseModel):
    model_config = ConfigDict(frozen=True)

    name: str
    age: int

user = ImmutableUser(name="John", age=30)
user.age = 31  # 에러 발생: Instance is frozen

# frozen=True이면 해시 가능
users_set = {user}  # 정상 작동

별칭 (Alias) 설정

옵션 타입 기본값 설명
populate_by_name bool False 필드명과 별칭 모두로 값 설정 허용 (deprecated)
validate_by_alias bool True 별칭으로 필드 값 설정 허용
validate_by_name bool False 별칭이 있어도 필드명으로 값 설정 허용
serialize_by_alias bool False 직렬화 시 별칭 사용
alias_generator Callable[[str], str] | None None 별칭 자동 생성 함수
loc_by_alias bool True 에러 위치에 별칭 사용

예시:

from pydantic import Field

class APIResponse(BaseModel):
    model_config = ConfigDict(
        validate_by_alias=True,
        validate_by_name=True,
        serialize_by_alias=True
    )

    user_name: str = Field(alias="userName")

# 둘 다 가능
response1 = APIResponse(userName="John")
response2 = APIResponse(user_name="John")

print(response1.model_dump(by_alias=True))  # {"userName": "John"}

예시 - alias_generator:

def to_camel(name: str) -> str:
    parts = name.split('_')
    return parts[0] + ''.join(word.capitalize() for word in parts[1:])

class CamelModel(BaseModel):
    model_config = ConfigDict(
        alias_generator=to_camel,
        serialize_by_alias=True
    )

    first_name: str
    last_name: str

data = CamelModel(firstName="John", lastName="Doe")
print(data.model_dump(by_alias=True))
# {"firstName": "John", "lastName": "Doe"}

JSON 스키마

옵션 타입 기본값 설명
title str | None None JSON 스키마 타이틀
json_schema_extra dict | Callable | None None JSON 스키마에 추가할 정보
json_schema_serialization_defaults_required bool False 직렬화 스키마에서 기본값이 있는 필드도 required로 표시
json_schema_mode_override Literal['validation', 'serialization', None] None JSON 스키마 모드 강제 지정

예시 - json_schema_extra:

class Product(BaseModel):
    model_config = ConfigDict(
        title="상품 정보",
        json_schema_extra={
            "example": {
                "name": "노트북",
                "price": 1500000
            },
            "description": "상품 데이터를 나타내는 모델"
        }
    )

    name: str
    price: int

# OpenAPI/Swagger 문서에 예시가 표시됨

예시 - Callable json_schema_extra:

def add_examples(schema: dict) -> dict:
    schema["examples"] = [
        {"name": "예시1", "value": 100},
        {"name": "예시2", "value": 200}
    ]
    return schema

class DynamicSchema(BaseModel):
    model_config = ConfigDict(json_schema_extra=add_examples)

    name: str
    value: int

ORM/속성 모드

옵션 타입 기본값 설명
from_attributes bool False 객체 속성에서 모델 생성 허용 (SQLAlchemy 등)

예시:

class UserORM:
    def __init__(self, name: str, age: int):
        self.name = name
        self.age = age

class UserModel(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    name: str
    age: int

orm_user = UserORM(name="John", age=30)
pydantic_user = UserModel.model_validate(orm_user)
print(pydantic_user)  # name='John' age=30

Enum 처리

옵션 타입 기본값 설명
use_enum_values bool False Enum 대신 값(value)으로 저장

예시:

from enum import Enum

class Status(Enum):
    ACTIVE = "active"
    INACTIVE = "inactive"

class User(BaseModel):
    model_config = ConfigDict(use_enum_values=True)

    status: Status

user = User(status=Status.ACTIVE)
print(user.status)  # "active" (문자열)
print(type(user.status))  # <class 'str'>

# use_enum_values=False (기본값)이면
# user.status는 Status.ACTIVE (Enum 객체)

직렬화 설정

옵션 타입 기본값 설명
ser_json_timedelta 'iso8601' | 'float' 'iso8601' timedelta JSON 직렬화 형식
ser_json_bytes 'utf8' | 'base64' | 'hex' 'utf8' bytes JSON 직렬화 인코딩
ser_json_inf_nan 'null' | 'constants' | 'strings' 'null' 무한대/NaN JSON 직렬화 형식

숫자/Float 설정

옵션 타입 기본값 설명
allow_inf_nan bool True float에서 무한대/NaN 허용
coerce_numbers_to_str bool False 숫자를 문자열로 강제 변환 허용

기타 설정

옵션 타입 기본값 설명
protected_namespaces tuple[str, ...] ('model_',) 보호할 필드명 접두사
hide_input_in_errors bool False 에러 메시지에서 입력값 숨김
defer_build bool False validator/serializer 빌드 지연
use_attribute_docstrings bool False 속성 docstring을 필드 설명으로 사용
regex_engine 'rust-regex' | 'python-re' 'rust-regex' 정규식 엔진 선택
validation_error_cause bool False Python 예외를 에러 원인에 포함

설정 상속

자식 모델은 부모 모델의 model_config를 상속받습니다.

class ParentModel(BaseModel):
    model_config = ConfigDict(
        str_strip_whitespace=True,
        extra='allow'
    )

    name: str

class ChildModel(ParentModel):
    model_config = ConfigDict(
        frozen=True  # 부모 설정 + frozen=True
    )

    age: int

# ChildModel은 str_strip_whitespace=True, extra='allow', frozen=True

FastAPI와 함께 사용

FastAPI에서 요청/응답 스키마로 사용할 때 특히 유용합니다.

from fastapi import FastAPI
from pydantic import BaseModel, ConfigDict, Field

app = FastAPI()

class CreateUserRequest(BaseModel):
    model_config = ConfigDict(
        str_strip_whitespace=True,
        json_schema_extra={
            "example": {
                "username": "johndoe",
                "email": "john@example.com"
            }
        }
    )

    username: str = Field(..., min_length=3, max_length=50)
    email: str

class UserResponse(BaseModel):
    model_config = ConfigDict(
        from_attributes=True,  # ORM 객체에서 변환 가능
        serialize_by_alias=True
    )

    id: int
    user_name: str = Field(alias="userName")

@app.post("/users", response_model=UserResponse)
async def create_user(user: CreateUserRequest):
    # user.username은 자동으로 공백이 제거됨
    ...

주의사항

  1. v1에서 마이그레이션: class Config는 deprecated입니다. model_config = ConfigDict(...)를 사용하세요.

  2. populate_by_name은 deprecated: validate_by_aliasvalidate_by_name을 함께 사용하세요.

  3. json_encoders는 deprecated: 커스텀 직렬화가 필요하면 @field_serializer 데코레이터를 사용하세요.

참고 자료