FastAPIで始めるPydantic入門
前回の記事では Pydantic を書き切れなかったので別でまとめました
FastAPIで始めるWeb API開発入門
の第2弾です
はじめに
FastAPIを学び始めると頻繁に登場するのが Pydantic です。
Pydantic は単なるバリデーションライブラリではありません。
- リクエストデータの検証
- レスポンスデータの定義
- OpenAPI(Swagger)の自動生成
- 型安全な開発
- エディタ補完の向上
- ReactやVueで扱いやすくなる
など、FastAPIの中核を支える重要な役割を持っています。
この記事では、FastAPIで実際によく利用するPydanticの機能を中心に解説します。
またSQLAlchemyでの応用例も説明していきます。
Pydanticとは
PydanticはPythonの型ヒントを利用してデータ検証を行うライブラリです。
from pydantic import BaseModel
class UserCreate(BaseModel):
name: str
age: int
型を宣言するだけで入力値の検証が行われます。
FastAPIではリクエストやレスポンスの定義に利用されます。
リクエストモデルの作成
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class UserCreate(BaseModel):
name: str
age: int
@app.post('/users')
def create_user(user: UserCreate):
return user
送信JSON
{
"name": "yamada",
"age": 20
}
Pydanticが自動的に検証を行います。
型チェック
{
"name": "yamada",
"age": "abc"
}
上記を送信すると422エラーになります。
{
"detail": [
{
"msg": "Input should be a valid integer"
}
]
}
FastAPIでは型検証エラーを自動で処理してくれます。
Optionalな項目
from typing import Optional
class UserCreate(BaseModel):
name: str
email: Optional[str] = None
またはPython3.10以降なら
class UserCreate(BaseModel):
name: str
email: str | None = None
Fieldを利用した入力制約
from pydantic import BaseModel, Field
class UserCreate(BaseModel):
name: str = Field(
...,
min_length=1,
max_length=50,
description='ユーザー名'
)
age: int = Field(
...,
ge=0,
le=120
)
主な制約
| 属性 | 内容 |
|---|---|
| min_length | 最小文字数 |
| max_length | 最大文字数 |
| ge | 以上 |
| gt | より大きい |
| le | 以下 |
| lt | 未満 |
|
Fieldを利用するとSwaggerにも反映されます。
EmailStrによるメールアドレス検証
外部ライブラリが必要なためインストールします
pip install "pydantic[email]"
# または
pip install email-validator
実際に動かしてみます
from pydantic import BaseModel, EmailStr
class UserCreate(BaseModel):
name: str
email: EmailStr
正常
{
"email": "test@example.com"
}
異常
{
"email": "abc"
}
ネストしたモデル
from pydantic import BaseModel
class Address(BaseModel):
zipcode: str
prefecture: str
class UserCreate(BaseModel):
name: str
address: Address
JSON
{
"name": "yamada",
"address": {
"zipcode": "1234567",
"prefecture": "東京都"
}
}
複雑なJSONでも簡単にマッピングできます。
List型
from pydantic import BaseModel
class User(BaseModel):
name: str
class UserList(BaseModel):
users: list[User]
JSON
{
"users": [
{"name": "yamada"},
{"name": "suzuki"}
]
}
ResponseModelとは
ResponseModelはレスポンスの型を定義する機能です。
class UserResponse(BaseModel):
id: int
name: str
ResponseModelを利用する
from pydantic import BaseModel
class UserResponse(BaseModel):
id: int
name: str
@app.get('/users/{user_id}')
def get_user(user_id: int) -> UserResponse:
return {
'id': user_id,
'name': 'yamada',
'password': 'secret'
}
実際のレスポンス
{
"id": 1,
"name": "yamada"
}
passwordは除外されます。
ResponseModelを使うべき理由
API仕様が明確になる
Swaggerにレスポンス構造が表示されます。
情報漏洩を防げる
DB情報をそのまま返却する事故を防げます。
フロントエンドとの契約になる
ReactやVueなどで扱いやすくなります。
入力用と出力用を分離する
推奨構成です。
class UserCreate(BaseModel):
name: str
password: str
class UserResponse(BaseModel):
id: int
name: str
入力と出力を明確に分離します。
更新用モデル
PATCHやPUTでよく利用します。
class UserUpdate(BaseModel):
name: str | None = None
email: str | None = None
更新したい項目のみ送信できます。
Pydantic v2のmodel_dump()
v2ではdict()ではなくmodel_dump()が推奨されています。
user = UserCreate(
name='yamada',
age=20
)
print(user.model_dump())
結果
{
'name': 'yamada',
'age': 20
}
SQLAlchemyとの組み合わせ
実務では頻繁に利用します。
from pydantic import BaseModel, ConfigDict
class UserResponse(BaseModel):
# ORM等のオブジェクトから属性ベースで値を読み込む設定を追加
model_config = ConfigDict(from_attributes=True)
id: int
name: str
email: str
SQLAlchemyの結果をResponseModelへ変換して返却します。
return UserResponse.model_validate(user)
実務でよくある構成
class UserCreate(BaseModel):
name: str
email: EmailStr
class UserUpdate(BaseModel):
name: str | None = None
email: EmailStr | None = None
class UserResponse(BaseModel):
id: int
name: str
email: EmailStr
多くの業務システムで
- Create
- Update
- Response
の3モデル構成が採用されています。
まとめ
Pydanticを利用すると
- 型チェック
- バリデーション
- OpenAPI生成
- レスポンス制御
- IDE補完
を簡単に実装できます。
まずは以下を覚えることをおすすめします。
- BaseModel
- Field
- EmailStr
- ResponseModel
- model_dump()
- Create / Update / Response の分離
これらを理解するとFastAPIでの実務開発がかなり楽になります。