목차
1. Pydantic
1.1. Pydantic 이란?
Pydantic은 파이썬 데이터 클래스와 유사한 구조로 데이터 모델을 정의하고, 이를 통해 데이터 검증과 변환을 수행하는 라이브러리입니다. 특히, 데이터의 유효성을 보장하고 자동으로 타입을 변환해주는 기능을 제공함으로써, 신뢰성 있는 애플리케이션 개발에 도움을 줍니다.
Pydantic 사용의 장점
• 코드의 간결성: 데이터 검증, 변환 로직을 수동으로 작성할 필요 없이, Pydantic 모델을 정의함으로써 자동으로 처리할 수 있어 코드가 간결해집니다.
• 안정성: FastAPI와 같은 웹 프레임워크에서는 외부에서 들어오는 데이터가 많기 때문에, 신뢰할 수 있는 데이터 검증이 필수적입니다. Pydantic은 이러한 역할을 수행함으로써 애플리케이션의 안정성을 높입니다.
• 타입 힌트와의 연계: Pydantic은 파이썬의 타입 힌트를 적극적으로 활용하여, 개발자가 원하는 데이터 구조를 명확하게 표현할 수 있습니다. 이는 IDE의 자동 완성 및 타입 검사를 통해 개발 생산성을 높여줍니다.
• 확장성: Pydantic의 모델은 매우 유연하고 확장 가능하여, 다양한 형태의 데이터 구조를 지원하고, 필요에 따라 유효성 검사를 커스터마이즈할 수 있습니다.
1.2. Pydantic의 기능
• 자동 데이터 변환: 잘못된 타입의 데이터를 올바른 타입으로 자동으로 변환해 주며, 예외가 발생하지 않도록 합니다.
• 자동 데이터 검증: 클라이언트가 전송한 요청 데이터가 정의된 스키마에 맞는지 자동으로 검증합니다.
• 유효성 검사: 특정 필드가 필수인지, 값이 올바른 범위 내에 있는지 등 세부적인 유효성 검사를 간편하게 수행할 수 있습니다.
• 중첩 모델: Pydantic 모델은 다른 Pydantic 모델을 필드로 포함할 수 있어, 복잡한 데이터 구조를 쉽게 관리할 수 있습니다.
• 환경 설정 관리: Pydantic의 BaseSettings 클래스를 사용하여 환경 변수나 설정 파일에서 설정 값을 가져와 관리할 수 있습니다.
2. Pydantic 모델 생성
2.1. BaseModel 클래스
- BaseModel 클래스
- Pydantic의 BaseModel 클래스는 데이터 모델을 정의하는 기본 클래스
- 이 클래스를 상속받아 새로운 모델을 정의
- 모델이 정의된 데이터에 대해 자동으로 유효성 검사를 수행
- 필요시 데이터를 변환하는 기능을 제공
2.2. BaseModel 클래스 사용 예제
- 클래스의 속성으로 필드 정의
- 필드에는 기본값을 설정할 수도 있으며, 기본값이 설정되지 않은 필드는 필수 입력 값으로 간주
- 기본값이 없는 필드는 해당 필드에 값이 제공되지 않으면 검증 시 오류
- 타입 힌트를 기반으로 데이터 검증을 수행
- 필드의 이름과 함께 타입을 지정하면, Pydantic은 해당 필드에 대한 데이터 검증을 수행
from pydantic import BaseModel
class Product(BaseModel):
name: str
price: float = 0.0 # 기본값 0.0
in_stock: bool = True # 기본값 True
객체 생성
user = Product(name='test', price=100.0, in_stock=True)
user
2.3. Field 사용하기
Field()를 사용하여 기본값과 추가적인 설명을 명시적으로 설정하는 방법은 데이터 검증 및 문서화에 유리합니다. 아래는 주어진 코드를 Field()를 사용하여 변경한 예시입니다
from pydantic import BaseModel, Field
class Product(BaseModel):
name: str = Field(..., description="제품 이름") # 필수 필드이므로 '...' 사용
price: float = Field(0.0, description="제품 가격, 기본값은 0.0") # 기본값 0.0
in_stock: bool = Field(True, description="재고 여부, 기본값은 True") # 기본값 True
3. 데이터 검증
3.1. 기본 데이터 검증
만약 타입 힘트에 맞지 않는 데이터가 들어올 시, ValidationError 발생
from pydantic import ValidationError
try:
user = User(id=123, name="Alice", price=100, in_stock=True)
except ValidationError as e:
print(e)
3.2. 유효성 검사 도구
3.2.1 정규 표현식을 사용한 문자열 검증
정규표현식 형식을 검증
Field에서 pattern을 사용
아래 예시코드는 이메일 형식이 올바른지 자동으로 검증
from pydantic import BaseModel, Field
class User(BaseModel):
email: str = Field(..., pattern=r'^\S+@\S+\.\S+$')
3.2.2. 문자열 검증
EmailStr : 이메일 주소 형식을 검증
from pydantic import BaseModel, EmailStr
class User(BaseModel):
email: EmailStr
AnyUrl: http, https, ftp 등의 다양한 URL 형식을 허용하는 타입
HttpUrl: http와 https로 시작하는 URL을 검증하는 타입
from pydantic import BaseModel, AnyUrl, HttpUrl
class Website(BaseModel):
url: AnyUrl
class SecureWebsite(BaseModel):
secure_url: HttpUrl
# 올바른 URL
site = Website(url="ftp://example.com/resource")
secure_site = SecureWebsite(secure_url="https://example.com")
# 잘못된 URL
try:
site = Website(url="not_a_url")
except ValidationError as e:
print(e)
3.2.3. strict 검증
StrictStr, StrictInt, StrictBool, StrictFloat: 이 타입들은 해당 필드가 정확히 지정된 타입인지 엄격하게 검증,
타입 변환을 시도하지 않으며, 입력된 데이터가 정확히 지정된 타입과 일치하지 않으면 오류를 발생
from pydantic import BaseModel, StrictStr, StrictInt
class Product(BaseModel):
name: StrictStr
quantity: StrictInt
3.2.4. 범위 검증
StrictStr, StrictInt, StrictBool, StrictFloat
필드에 대해 특정 범위 내에 있는지 검증
from pydantic import BaseModel, constr, conint
class User(BaseModel):
username: constr(min_length=3, max_length=20)
age: conint(gt=0, le=120)
3.2.5. 필수 필드와 선택 필드
모든 필드가 필수는 아님
Optional 타입 힌트를 사용해 선택적으로 필드를 사용할 수 있도록 함.
from typing import Optional
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
age: Optional[int] = None # 선택적 필드
email: str
데이터 실행
User(id=1, name="test", email="abc@google.com")
3.3. 커스텀 데이터 검증
사용자 정의 검증 로직 추가
- @validator 데코레이터를 사용
나이가 양수인지 확인하는 검증 예시코드
from pydantic import BaseModel, field_validator, ValidationInfo
class User(BaseModel):
age: int
@field_validator('age')
def validate_age(cls, v: int) -> int: # cls 없이 사용 가능
if v < 0:
raise ValueError("나이는 0 이상이어야 합니다.")
return v
여러 필드를 동시에 검증
- @model_validator는 전체 모델의 데이터에 접근할 수 있음
from pydantic import BaseModel, model_validator
class Model(BaseModel):
x: str
y: int
@model_validator(mode='before')
def validate_model(cls, values):
if values['x'] == 'error':
raise ValueError("x에 잘못된 값이 들어갔습니다.")
return values
Model(x="valid", y=123)
4. Pydantic 모델의 활용
4.1. Get의 쿼리 매개변수
`Depends()`를 사용하여 FastAPI는 해당 쿼리 매개변수를 모델에 맞춰 자동으로 매핑하고, 모델의 검증 기능을 활용
from fastapi import FastAPI, Depends
from pydantic import BaseModel
app = FastAPI()
# 쿼리 매개변수를 위한 Pydantic 모델 정의
class ItemQueryParams(BaseModel):
name: str
price_min: float = 0
price_max: float = 1000
# 쿼리 매개변수를 Pydantic 모델로 처리
@app.get("/items/")
async def read_items(params: ItemQueryParams = Depends()):
return {"name": params.name, "price_min": params.price_min, "price_max": params.price_max}
4.2. Post에서 Request Body 부분
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
id: int
name: str
price: float
# Item 모델을 사용하여 요청 바디로 데이터를 받는 엔드포인트
@app.post("/items/")
async def create_item(item: Item):
return item
5. 모델 중첩
필드의 타입으로 다른 모델을 이용할 수 있음
from pydantic import BaseModel
# Address 모델 정의
class Address(BaseModel):
street: str
city: str
zipcode: str
# User 모델 정의 (Address 모델을 포함)
class User(BaseModel):
name: str
age: int
address: Address # Address 모델을 필드로 포함
아래와 같이 모델에 데이터를 넣음
# 중첩된 데이터 구조를 사용하여 User 모델 인스턴스 생성
user_data = {
"name": "Alice",
"age": 30,
"address": {
"street": "123 Main St",
"city": "Wonderland",
"zipcode": "12345"
}
}
user = User(**user_data)
print(user)
6. ORM 통합
6.1. Pydantic 모델과 ORM 모델의 차이점
• Pydantic 모델: 주로 데이터 검증, 변환, API 요청/응답 처리에 사용됩니다. Pydantic은 Python의 데이터 클래스와 유사하게 동작하며, JSON과 같은 데이터를 쉽게 처리할 수 있습니다.
• ORM 모델: 데이터베이스와 상호 작용하기 위해 사용됩니다. ORM 모델은 데이터베이스 테이블과 직접 매핑되며, 데이터를 저장, 조회, 업데이트하는 기능을 제공합니다.
6.2. Pydantic과 SQLAlchemy 모델의 통합
Pydantic 모델과 SQLAlchemy 모델을 함께 사용하는 주요 목적은 다음과 같습니다.
- 데이터베이스의 데이터를 검증된 API 응답으로 변환: 데이터베이스에서 가져온 데이터를 Pydantic 모델을 통해 API 응답으로 제공할 때, 데이터 검증과 직렬화를 자동으로 처리할 수 있습니다.
- API 요청 데이터를 데이터베이스로 저장: 클라이언트로부터 받은 데이터를 Pydantic 모델로 검증한 후, 이를 SQLAlchemy 모델로 변환하여 데이터베이스에 저장할 수 있습니다.
6.3. 예시 코드
1. SQLAlchemy 모델 정의
from sqlalchemy import Column, Integer, String, create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
Base = declarative_base()
class UserORM(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True, index=True)
name = Column(String, index=True)
email = Column(String, unique=True, index=True)
# 데이터베이스 연결 설정
DATABASE_URL = "sqlite:///./test.db"
engine = create_engine(DATABASE_URL)
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
2. Pydantic 모델 정의
- UserBase: 공통 필드를 정의한 기본 Pydantic 모델입니다.
- UserCreate: 데이터를 생성할 때 사용할 Pydantic 모델입니다.
- User: 데이터베이스에서 조회한 데이터를 응답할 때 사용할 Pydantic 모델입니다.
from pydantic import BaseModel
class UserBase(BaseModel):
name: str
email: str
class UserCreate(UserBase):
pass # 추가로 필드를 정의하거나 기능을 확장할 수 있음
class User(UserBase):
id: int
# ORM 통합 모드 활성화
model_config = ConfigDict(from_attributes=True)
3. ORM 통합을 위한 orm_mode
- Pydantic 모델에 Config 클래스를 정의하고 orm_mode = True로 설정
- 이를 통해 SQLAlchemy 모델의 데이터를 Pydantic 모델로 변환할 수 있음
pydantic 모델 -> orm 모델 (딕셔너리로 변경 후 넣어줌)
def pydantic_to_orm(pydantic_model: UserCreate) -> UserORM:
data = pydantic_model.model_dump() # dictionary로 변경
return orm_class(**data) # 딕셔너리를 orm_class로 변경
orm 모델 -> pydantic 모델 (from_orm 메소드 사용)
user_orm = UserORM(id=1, name="Alice", email="alice@example.com")
user_pydantic = User.model_validate(user_orm) # orm -> pydantic
print(user_pydantic.model_dump_json()) # JSON 변환
4. Post 에서 사용
client 요청 -> pydantic 모델 -> orm 모델
from fastapi import FastAPI, Depends
from sqlalchemy.orm import Session
app = FastAPI()
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/users/", response_model=User)
def create_user(user: UserCreate, db: Session = Depends(get_db)):
db_user = UserORM(name=user.name, email=user.email)
db.add(db_user)
db.commit()
db.refresh(db_user)
return User.model_validate(db_user)
5. Get 에서 사용
데이터베이스 -> ORM 모델 -> Pydantic 모델
@app.get("/users/{user_id}", response_model=User)
def read_user(user_id: int, db: Session = Depends(get_db)):
db_user = db.query(UserORM).filter(UserORM.id == user_id).first()
return User.model_validate(db_user)
7. Pydantic 설정 관리
pydantic은 애플리케이션 설정을 관리하기 위한 강력한 도구를 제공함. 특히, 환경 변수나 설정 파일을 사용하여 애플리케이션의 설정을 중앙에서 관리하고, 이를 코드와 분리하는 것이 중요. Pydantic의 BaseSettings 클래스를 사용하면 이러한 설정을 간편하게 관리할 수 있다.
7.1. settings 관리
- 보안: 중요한 설정(예: 데이터베이스 URL, API 키)을 코드에서 분리
- 유지보수성: 설정이 중앙화되어 있어 관리가 쉬워짐
- 유연성: 환경에 따라 설정을 변경하기 쉽습니다(예: 개발 환경과 배포 환경에서 다른 설정 사용).
env_file 설정을 통해 .env 파일에서 설정 값을 로드할 수 있음
from pydantic import BaseSettings, ConfigDict
class Settings(BaseSettings):
app_name: str = "My FastAPI Application"
debug: bool = False
database_url: str
# 최신 설정 방식: ConfigDict 사용
model_config = ConfigDict(
case_sensitive=False, # 대소문자 구분 안 함
env_file=".env", # .env 파일 경로 지정
)
# 설정 인스턴스를 생성
settings = Settings()
print(settings.app_name) # 출력: My FastAPI Application
print(settings.debug) # 출력: False
print(settings.database_url) # 환경 변수나 .env 파일에서 가져온 값
BaseSettings 클래스에서 설정 필드의 값은 다음과 같은 우선순위로 결정
1. 환경 변수: 시스템 환경 변수에 설정된 값이 가장 높은 우선순위를 가집니다.
2. 설정 파일: .env 파일이나 다른 설정 파일에서 로드된 값이 그다음 우선순위를 가집니다.
3. 클래스의 기본값: 클래스에서 정의한 기본값이 우선순위의 마지막을 차지합니다.
7.2. 설정 값의 동적 로드
from pydantic import BaseSettings, ConfigDict
# 환경 파일 동적 설정
env_file = ".env.dev" if __debug__ else ".env.prod"
class Settings(BaseSettings):
app_name: str
debug: bool
# 최신 설정 방식: ConfigDict 사용
model_config = ConfigDict(
env_file=env_file # 동적으로 선택된 환경 파일 지정
)
settings = Settings()
print(settings.app_name) # .env.dev 또는 .env.prod에서 로드된 값
print(settings.debug) # .env.dev 또는 .env.prod에서 로드된 값
'Fastapi' 카테고리의 다른 글
[FastAPI] SSO 로그인과 OAuth 2.0 (0) | 2024.11.16 |
---|---|
[FastAPI] Uvicorn 과 Gunicorn 사용하기 (0) | 2024.11.14 |
[FastAPI] 비동기(Asynchronous)프로그래밍 (4) (0) | 2024.08.24 |
[FastAPI] HTML 사용해 보기 (3) (0) | 2024.08.22 |
[FastAPI] get post (2) (0) | 2024.08.17 |