Python

Pydanticとは?読み方・BaseModelの使い方とV1との違い【2.13対応】

Pydanticとは?読み方・BaseModelの使い方とV1との違い【2.13対応】

Pydantic(パイダンティック)は、Pythonの型ヒントを書くだけで、入力データの検証と型変換をまとめて行うライブラリです。BaseModel を継承したクラスに id: int と書けば、文字列の "42" は整数の42に変換され、"abc" はどのフィールドがなぜ不正かを示す ValidationError になります。本記事のコードは Pydantic 2.13.5(2026年8月28日公開、2026年9月28日時点の最新安定版)と Python 3.13 で実行して確認し、出力は必要な部分を抜粋して載せています。

まとめ:Pydanticの要点

  • 型ヒントを検証ルールとして使うデータ検証ライブラリ。読み方は「パイダンティック」
  • 最新安定版は2.13.5。V2から検証の中核(pydantic-core)がRust製になった
  • 基本は BaseModel を継承し、制約は Field()、独自ルールは field_validator と model_validator で書く
  • JSONの読み込みは model_validate_json()、書き出しは model_dump_json()。V1の parse_obj()・.dict()・.json() は非推奨
  • 既定では “42”→42 のような変換をするが、V2は 4.9→4 の切り捨てや 123→”123″ の変換をしない

以下、読み方と位置付け、最小のコード、自動変換の範囲、バリデーター、JSONとの変換、使いどころの順に説明します。

Pydanticの位置付けと読み方

名前の由来と読み方

公式ドキュメントは、Pydanticという名前を「Py」と「pedantic」のかばん語(2語を縮めてつないだ語)だと説明しています。pedanticは「細かいことにうるさい、杓子定規な」という意味の英単語で、データの型に細かく口を出すライブラリの性格をそのまま表しています。

日本語では「パイダンティック」と読み、表記するのが一般的です。公式が日本語の読みを定めているわけではありませんが、pedanticの英語の発音(ペダンティック)に、Pythonの「パイ」を重ねた読み方です。

最新版2.13とRust製のpydantic-core

PyPIで配布されている最新の安定版は2.13.5(2026年8月28日)で、動作要件は Python 3.9 以上です。次期の2.14は2026年9月9日に2.14.0b2が出たベータ段階で、2.14系ではPython 3.9のサポートが打ち切られ、3.10以上が必要になります。3.9の環境を残している場合、2.13系が最後の対応版です。

2023年のV2で内部が作り直され、検証処理の中核は別パッケージの pydantic-core(Rust製)に移りました。公式ドキュメントは「Pydanticの検証ロジックの中核はRustで書かれている」と説明し、V2の発表時には「V1より約17倍速い」としていました。Web上の古い解説に「Cythonで高速化」とあるのはV1時代の話です。2.13.5には pydantic-core 2.46.5 が組み合わされます。

利用規模について、2026年9月28日に確認した公式のGet Startedページは「月5億5,000万回以上ダウンロードされ、PyPI上の約8,000パッケージが利用している」と書いており、FastAPI・Hugging Face・SQLModel・LangChain が例に挙がっています。

型ヒント・dataclassとの違い

Pythonの型ヒントそのものは実行時に何も検査しません。標準ライブラリの dataclass も同じで、次のように int と宣言したフィールドに文字列を渡しても、そのまま通ります。

from dataclasses import dataclass

@dataclass
class DP:
    x: int
    y: str

print(DP(x="abc", y=1))
# DP(x='abc', y=1)

mypyやPylanceのような静的型チェッカーは、コード中で型に合わない値を渡す誤りを実行前に見つけます。ただし、APIのリクエストやCSV、環境変数のように実行時に外から入ってくる値は対象外です。Pydanticはこの実行時の境界を受け持ちます。型ヒントは同じ書き方のまま、外部入力を検証・変換するのがPydanticの役割です。

手段 検査の時点 型変換 JSON入出力
型ヒント+mypy 実行前 しない なし
dataclass 検査しない しない なし(自作)
TypedDict 実行前のみ しない なし
Pydantic BaseModel 実行時 する(laxモード) 組み込み

dataclassやTypedDictのままPydanticの検証だけ使うこともできます。pydantic.dataclasses.dataclass や TypeAdapter がその入口です。名前付きタプルやdataclassの選び分けはnamedtupleとdataclass・dictの違いで扱っています。

インストールとBaseModelの基本形

本記事と同じ版を使う場合は python -m pip install "pydantic==2.13.5" でインストールします。メールアドレス型(EmailStr)を使うときは pip install "pydantic[email]" で依存を追加します。次が最小の例です。

from datetime import date
from pydantic import BaseModel, Field, ValidationError

class User(BaseModel):
    id: int
    name: str = Field(min_length=1, max_length=20)
    email: str
    signup: date
    tags: list[str] = []

u = User(id="42", name="Sato", email="[email protected]", signup="2026-09-28")
print(repr(u))
# User(id=42, name='Sato', email='[email protected]', signup=datetime.date(2026, 9, 28), tags=[])
print(u.model_dump())
# {'id': 42, 'name': 'Sato', 'email': '[email protected]', 'signup': datetime.date(2026, 9, 28), 'tags': []}

文字列で渡した id は int に、signup は datetime.date に変換されています。Field() は既定値と制約をまとめて書く関数で、min_length・max_length(文字列やリストの長さ)、gt・ge・lt・le(数値の範囲)、pattern(正規表現)、alias(外部データ側のキー名)がよく使う引数です。

tags: list[str] = [] のように可変の既定値を直接書けるのも、dataclassとの違いです。Pydanticはリストなどハッシュ化できない既定値をインスタンスごとにディープコピーするため、この例ではリストが共有されません。

ValidationErrorの読み方

不正な値を渡すと、Pydanticは最初のエラーで止まらず、すべてのフィールドを検査してからまとめて報告します。

try:
    User(id="abc", name="", email="x", signup="2026-13-01")
except ValidationError as e:
    print(e.error_count())  # 3
    print(e)
3 validation errors for User
id
  Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='abc', input_type=str]
    For further information visit https://errors.pydantic.dev/2.13/v/int_parsing
name
  String should have at least 1 character [type=string_too_short, input_value='', input_type=str]
    For further information visit https://errors.pydantic.dev/2.13/v/string_too_short
signup
  Input should be a valid date or datetime, month value is outside expected range of 1-12 [type=date_from_datetime_parsing, input_value='2026-13-01', input_type=str]
    For further information visit https://errors.pydantic.dev/2.13/v/date_from_datetime_parsing

各エラーは「フィールド名」「英語のメッセージ」「type= のエラー種別」の3点で読みます。email="x" はエラーになっていません。str と宣言しただけでは形式を検査しないためで、メール形式を強制するには EmailStr を使います。

プログラムで分岐するときは、メッセージ文ではなく e.errors() が返す辞書の type と loc を見ます。メッセージは版で変わることがありますが、int_parsing のような種別名は変わりにくいためです。

try:
    User(id="abc", name="", email="x", signup="2026-13-01")
except ValidationError as e:
    print(e.errors()[0])
# {'type': 'int_parsing', 'loc': ('id',), 'msg': 'Input should be a valid integer, unable to parse string as an integer', 'input': 'abc', 'url': 'https://errors.pydantic.dev/2.13/v/int_parsing'}

自動変換の範囲:laxモードの実測とV1との差

Pydanticは既定で「laxモード」で動き、型ごとの変換規則に従って入力を宣言した型に変換します。ただし、変換で情報が失われる場合もあります。どこまでが変換され、どこからがエラーかは、ドキュメントを読むより表で見たほうが早いので、TypeAdapter(型).validate_python(値) で1件ずつ確かめました。右列は strict=True を付けた場合です。

宣言した型 入力 laxモード(既定) strictモード
int “42” 42 int_type
int ” 42 “ 42 int_type
int “1_000” 1000 int_type
int 4.0 4 int_type
int 4.5 int_from_float int_type
int True 1 int_type
float 3 3.0 3.0
bool “yes” / “on” / “1” True bool_type
bool 2 bool_parsing bool_type
str 123 string_type string_type
str b”abc” “abc” string_type
date “2026-09-28” date(2026, 9, 28) date_type
date “2026/09/28” date_from_datetime_parsing date_type
list[int] (“1”, “2”) [1, 2] list_type

表で英字だけのセルは、エラーになったときの種別名です。見落としやすい点が3つあります。int 型に True を渡すと1として通ること、日付文字列の「2026/09/28」はエラーになる一方、ハイフン区切りの日付以外にも条件を満たすUnix時刻やdatetimeを受け付けること、そして str 型に数値の123を渡すとエラーになることです。

最後の点はV1からの変更です。同じ環境に同梱されている pydantic.v1 で試すと、V1は123を "123" に変換し、int 型に4.9を渡すと4に切り捨てていました。V2はどちらもエラーにします。V1時代の記事のコードがV2で動かない場合、この変換規則の差が原因の一つになります。

from pydantic.v1 import BaseModel as V1Model

class V1(V1Model):
    s: str
    i: int

print(V1(s=123, i=4.9))
# s='123' i=4   ← V2では s も i も検証エラー

変換を止めたいフィールドには Field(strict=True) を付けます。JSONを model_validate_json() で読む場合は、strictモードでも "2026-09-28" のような文字列から date への変換は許されます(JSONに日付型が無いため)。モデル全体へのstrictの掛け方とV1からの設定の移し替えは、Pydantic v2のConfigDict設定一覧にまとめています。

field_validatorとmodel_validatorによる独自ルール

型と Field() の制約で書けないルールはバリデーターで足します。1つのフィールドだけを見るなら @field_validator、複数のフィールドを突き合わせるなら @model_validator です。

from datetime import date
from pydantic import BaseModel, field_validator, model_validator

class Reservation(BaseModel):
    guest: str
    check_in: date
    check_out: date

    @field_validator("guest")
    @classmethod
    def strip_guest(cls, v: str) -> str:
        v = v.strip()
        if not v:
            raise ValueError("氏名が空です")
        return v

    @model_validator(mode="after")
    def check_dates(self):
        if self.check_out <= self.check_in:
            raise ValueError("check_out は check_in より後の日付にしてください")
        return self

print(Reservation(guest="  田中 ", check_in="2026-10-01", check_out="2026-10-03"))
# guest='田中' check_in=datetime.date(2026, 10, 1) check_out=datetime.date(2026, 10, 3)

バリデーターは ValueError を送出すれば検証エラーとして扱われます。上のfield_validatorは加工後のフィールド値を返し、mode=”after”のmodel_validatorは検証後のモデル自身を返します。上の例では前後の空白を落とした「田中」が保存されています。日付を逆にすると、Value error, check_out は check_in より後の日付にしてください [type=value_error, ...] という1件のエラーになります。

mode="after" は型変換が済んだ後に動くという意味で、self.check_in はすでに date 型です。変換前の生の値に手を入れたいときは mode="before" を使います。V1の @validator と @root_validator はV2では非推奨で、置き換え先が上の2つです。@root_validator は既定のまま(pre=False)書くとクラス定義の時点で PydanticUserError になり、skip_on_failure=True を付けないと動きません。

同じルールを複数のモデルで使うなら、デコレーターより Annotated で型に付けるほうが再利用しやすくなります。

from typing import Annotated
from pydantic import AfterValidator, TypeAdapter

def even(v: int) -> int:
    if v % 2:
        raise ValueError("偶数ではありません")
    return v

EvenInt = Annotated[int, AfterValidator(even)]
print(TypeAdapter(list[EvenInt]).validate_json("[2, 4, 6]"))  # [2, 4, 6]
# "[2, 3]" を渡すと loc=(1,) の位置で「偶数ではありません」

JSON・辞書との相互変換とJSON Schema

V2で名前が変わったメソッドの対応です。V1の書き方は2.13でも動きますが、呼ぶたびに非推奨の警告が出ます。

処理 V2(現行) V1(非推奨)
辞書から生成 Model.model_validate(d) Model.parse_obj(d)
JSON文字列から生成 Model.model_validate_json(s) Model.parse_raw(s)
辞書へ出力 m.model_dump() m.dict()
JSON文字列へ出力 m.model_dump_json() m.json()
JSON Schema出力 Model.model_json_schema() Model.schema()
検証なしで生成 Model.model_construct() Model.construct()

JSONを読むときは json.loads() で辞書にしてから model_validate() に渡すより、文字列のまま model_validate_json() に渡します。公式のパフォーマンスガイドも通常はこの書き方を勧めており、JSONの解析とモデルの検証がpydantic-coreの中で済みます。ただし、mode="before" や mode="wrap" のバリデーターを使う場合は、先に json.loads() で辞書にするほうが速くなることがあると同じガイドに書かれています。ネストしたモデルの場合、エラーの loc は深さまで示します。

from pydantic import BaseModel, Field

class Item(BaseModel):
    sku: str
    qty: int = Field(gt=0)

class Order(BaseModel):
    order_id: int
    items: list[Item]

Order.model_validate_json('{"order_id": 1, "items": [{"sku": "A-1", "qty": 2}, {"sku": "B-2", "qty": 0}]}')
# ValidationError: loc=('items', 1, 'qty') type=greater_than

2件目の明細の qty が0だと、('items', 1, 'qty') という経路で場所が返ります。APIのエラーレスポンスにそのまま載せられる形です。

model_dump_json() は date を "2026-09-28" のような文字列に変換して出力します。一方、model_dump() は date オブジェクトのまま返すので、辞書を json.dumps() に渡すと失敗します。JSONにしたいなら最初から model_dump_json() か model_dump(mode="json") を使います。

モデル定義からJSON Schemaも出せます。Item.model_json_schema() の結果は次のとおりで、Field(gt=0) が exclusiveMinimum に反映されています。

{"properties": {"sku": {"title": "Sku", "type": "string"}, "qty": {"exclusiveMinimum": 0, "title": "Qty", "type": "integer"}}, "required": ["sku", "qty"], "title": "Item", "type": "object"}

Pydanticが使われる場面

FastAPIのリクエストとレスポンス

FastAPIはエンドポイントの引数に BaseModel を書くと、リクエストボディの検証、422エラーの応答、OpenAPIドキュメントの生成をすべてPydanticのモデルから行います。FastAPIは0.126.0(2025年12月20日)で単体のPydantic V1のサポートを、0.128.0(2025年12月27日)で pydantic.v1 名前空間のサポートも終了しました。現行のFastAPIでは、V2の書き方で定義したモデルしか使えません。FastAPIの仕組みと始め方はFastAPIの概要記事で解説しています。データベースのテーブル定義とPydanticモデルを1つのクラスで兼ねたい場合は、SQLModelが選択肢になります。

環境変数と設定ファイル(pydantic-settings)

V1では本体に入っていた BaseSettings は、V2から別パッケージの pydantic-settings(pip install pydantic-settings)に移りました。環境変数を型付きで読み込み、"true" をboolに、"9000" をintに変換します。

from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="APP_")
    debug: bool = False
    port: int = 8000

# 環境変数 APP_DEBUG=true APP_PORT=9000 のとき
print(Settings())
# debug=True port=9000

LLM構造化出力でのJSON Schema生成と検証

生成AIのAPIでJSONを返させる用途でもPydanticは定番です。モデルクラスからJSON Schemaを作ってAPIに渡し、返ってきたJSONを同じクラスで検証します。OpenAIのPython SDK、LangChain、PydanticAIがこの形を採っています。仕組みと実装はLLM構造化出力の解説を参照してください。

Pydanticを使わないほうがよい場面

Pydanticは外部から来るデータの入口で使う道具で、アプリケーション内部の値の受け渡しにまで使う必要はありません。検証にはコストがかかります。この記事の実行環境(Intel Core i9-9880H・Python 3.13)で、x: int と y: str を持つモデルに同じ辞書をキーワード引数として渡し、timeit.repeat で20万回の生成を5回測定して最小値を採ったところ、1件あたり dataclass は約0.7〜0.8マイクロ秒、BaseModel は約3.0マイクロ秒でした。

1件ずつなら無視できる差ですが、すでに検証済みのデータを内部で何度も詰め替える処理や、数百万行を1行ずつモデル化するバッチでは積み上がります。次の場合はdataclassやTypedDictで十分です。

  • 自分のコードの中だけで作って使う値で、外部入力を含まない
  • 入口ですでにPydanticで検証したデータを、内部の関数間で受け渡すだけ
  • 大量の表形式データを扱い、列単位の検証(pandasやPolarsの型指定)で足りる

逆に、APIのリクエスト、設定値、外部サービスからのレスポンス、LLMの出力のように「中身を信用できないデータ」が入ってくる境界では、Pydanticで一度検証してから内部に渡す設計が崩れにくくなります。

よくある質問

Pydanticの読み方は?

日本語では「パイダンティック」と読むのが一般的です。公式ドキュメントは名前の由来を、Python の「Py」と、英語で「細かいことにうるさい」を意味する「pedantic」を組み合わせた語だと説明しています。

PydanticのBaseModelとは?

データの形を定義するための基底クラスです。継承したクラスに型ヒント付きでフィールドを書くと、インスタンス生成時に検証と型変換が行われ、model_dump() や model_dump_json() で辞書やJSONに書き出せるようになります。

PydanticのFieldの使い方は?

name: str = Field(min_length=1, max_length=20) のように、フィールドの既定値の位置に書きます。長さ・数値範囲・正規表現などの制約、外部データのキー名(alias)、JSON Schemaに出る説明文(description)を指定できます。

Pydanticとtypingの違いは?

typing は型を「書く」ための標準モジュールで、実行時には何も検査しません。Pydanticはその型ヒントを読み取り、実行時に値を検証・変換します。typingで書いた型を、Pydanticが実行時のルールとして使う関係です。

V1のコードはV2で動きますか?

.dict() や @validator などの多くは非推奨の警告付きで動きます。ただし、str 型への数値の変換や、int 型への小数の切り捨てのような型変換の規則はV2で厳しくなっており、V1で通っていた入力がエラーになることがあります。

関連記事

お気に入りに入れた記事の一覧

この記事は以下の記事からリンクされています

資料請求

今日のトレンド記事 直近 24 時間で、いつもより多く読まれている記事

  1. 2026.10.27 テックブログ Brotli 脆弱性:CVE-2026-111497の影響確認とNode.js・Python・.NETの更新手順
  2. 2026.10.28 テックブログ リンツ不正アクセス:314万件の対象確認とパスワード強制リセット後の対処
  3. 2026.10.27 テックブログ Joshin(ジョーシン)不正アクセス:最大600万件の対象とサブスク初期パスワードの対処
  4. 2026.10.23 テックブログ Renta!はいつ再開?不正アクセスでメールアドレス等が流出したおそれ・再開時のパスワード再設定と運営側の対応
  5. 2026.10.28 テックブログ 楽天トラベル不正アクセス:最大約260万件の対象確認とカード下4桁の詐欺対策

RELATED POSTS 関連記事

目次