43
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Qiita100万記事感謝祭!記事投稿キャンペーン開催のお知らせ

Pydantic入門 – Pythonでシンプルかつ強力なバリデーションを始めよう

Last updated at Posted at 2025-01-11

はじめに

この記事では、PythonのデータバリデーションライブラリであるPydanticを使って、簡単にかつ強力にデータのバリデーションを行う方法を解説します。
今回はGoogle Colab上でハンズオン形式で進めていきますので、ブラウザさえあれば実行環境を整えるのも簡単です。


1. Pydanticとは?

  • データのバリデーションや型の宣言を簡単に行えるPythonライブラリ
  • Pythonの型ヒント(type hints)を活用して、データ構造の定義と検証を同時に実現
  • FastAPIなどの人気フレームワークでも広く採用されており、API開発・プロジェクト構成などで非常に便利

Pydanticを使うと、辞書やJSONで受け取ったデータが正しい形式になっているかどうかをPythonicに検証できるようになります。データの整合性を保つために煩雑なチェックを書かなくてよくなるので、とてもおすすめです。

image.png


2. Google Colabでの開発準備

まずはGoogle Colabにアクセスし、新しいノートブックを作成します。無料のGoogleアカウントがあれば簡単に利用できます。

2-1. ライブラリのインストール

Google Colabのセルで次のようにして、Pydanticをインストールしましょう。Colab環境は再起動するとインストール済みのライブラリが消えてしまうため、ノートブックを開くたびに必要に応じてインストールします。

!pip install pydantic

3. はじめてのPydanticモデル作成

image.png

Pydanticの基本はBaseModelクラスを継承してモデルクラスを作ることです。以下の例では、ユーザープロファイルを表すモデルを定義します。

from pydantic import BaseModel

class User(BaseModel):
    name: str
    age: int
    email: str
  • BaseModelを継承したクラスに型ヒント付きで属性を定義する
  • インスタンス化するときに、型や必須項目のチェックが自動で行われる

3-1. 簡単な動作確認

実際にインスタンスを作成して動作を確認してみましょう。

# 正しいデータの場合
valid_data = {
    "name": "Alice",
    "age": 25,
    "email": "alice@example.com"
}
user = User(**valid_data)
print(user)
  • User(**valid_data)で辞書を展開して引数を渡します。
  • print(user)すると、name='Alice' age=25 email='alice@example.com'のようにバリデーションを通ったデータが表示されます。
# 間違ったデータの場合
invalid_data = {
    "name": "Bob",
    "age": "not a number",
    "email": "bob@example.com"
}
user = User(**invalid_data)
  • ageが文字列になっているためバリデーションエラー (ValidationError) が発生します。
  • エラーにはどのフィールドがどのように不正なのかが表示されるので、デバッグにも便利です。

4. バリデーションの仕組みと例外処理

Pydanticはインスタンス生成時にバリデーションを行い、不正なデータがあれば例外を投げます。エラーハンドリングしたい場合は、try-exceptで囲んで処理しましょう。

from pydantic import ValidationError

try:
    user = User(**invalid_data)
except ValidationError as e:
    print("Validation Error:")
    print(e)
  • ValidationErrorオブジェクトには、エラー詳細が格納されている
  • どのフィールドでバリデーションに失敗したかがわかりやすい

5. 追加バリデーション – カスタムバリデータ

Pydanticには、カスタムバリデータを定義するための便利な仕組みが用意されています。
たとえば、@validatorデコレータを使って、emailフィールドのドメインを限定するといったことが可能です。

from pydantic import BaseModel, validator

class User(BaseModel):
    name: str
    age: int
    email: str

    @validator('email')
    def email_must_be_certain_domain(cls, v):
        if not v.endswith("@example.com"):
            raise ValueError("Email must be on the 'example.com' domain.")
        return v
  • @validator('email')によって、emailフィールドに対するバリデーションを定義
  • 条件を満たさない場合はValueErrorを発生させる

試しに次のコードを実行すると、正しいアドレスと誤ったアドレスで結果が変わることを確認できます。

try:
    user_ok = User(name="Carol", age=30, email="carol@example.com")
    print("バリデーション成功:", user_ok)
    
    user_ng = User(name="Dave", age=40, email="dave@gmail.com")
except ValidationError as e:
    print("バリデーション失敗:", e)

image.png


6. モデル同士の入れ子(ネスト)を試してみる

複雑なデータ構造を扱いたい場合は、モデルを入れ子にすることができます。
たとえば、AddressモデルとUserモデルを別々に定義して、Userモデルの中にAddressモデルを持たせるイメージです。

from pydantic import BaseModel
from typing import Optional

class Address(BaseModel):
    city: str
    zip_code: str

class UserWithAddress(BaseModel):
    name: str
    age: int
    address: Optional[Address] = None
  • addressフィールドにはAddressを格納
  • Optional[Address]にすることで、住所情報が無い場合(None)でもOKに
data_with_address = {
    "name": "Emily",
    "age": 28,
    "address": {
        "city": "Tokyo",
        "zip_code": "100-0001"
    }
}

user_with_address = UserWithAddress(**data_with_address)
print(user_with_address)

7. まとめ

  • Pydanticを使うと、型ヒントを活用して簡潔にデータバリデーションが可能
  • ValidationErrorでエラー内容が明確になるため、デバッグやエラー処理もしやすい
  • Google Colabならすぐに環境を整えてハンズオンができる
  • さらに複雑なモデルやカスタムバリデーションを組み合わせることで、堅牢なデータ構造を実現できる

今回はPydanticの超入門編として、基本的な使い方を紹介しました。次回以降では、ユニオン型を使った柔軟なバリデーションや、設定クラスを使った細かい挙動制御、FastAPIなどでの活用方法などを取り上げていきたいと思います。

image.png


参考リンク

動作確認用ノートブック

{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "41150395",
   "metadata": {},
   "source": [
    "# Pydantic 動作確認ノートブック\n",
    "\n",
    "このノートブックでは、Pydanticの基本的な機能を動作確認するためのコードを実行します。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "34c16a2f",
   "metadata": {},
   "outputs": [],
   "source": [
    "!pip install pydantic"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8de996d2",
   "metadata": {},
   "source": [
    "## Pydanticモデルの作成\n",
    "\n",
    "まず、基本的なPydanticモデルを作成して動作を確認します。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "cab5fadb",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "from pydantic import BaseModel\n",
    "\n",
    "class User(BaseModel):\n",
    "    name: str\n",
    "    age: int\n",
    "    email: str\n",
    "\n",
    "# 正しいデータ\n",
    "valid_data = {\n",
    "    \"name\": \"Alice\",\n",
    "    \"age\": 25,\n",
    "    \"email\": \"alice@example.com\"\n",
    "}\n",
    "\n",
    "user = User(**valid_data)\n",
    "print(user)\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c56b40bc",
   "metadata": {},
   "source": [
    "## バリデーションエラーの確認\n",
    "\n",
    "不正なデータを与えるとバリデーションエラーが発生します。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "80789ccf",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "from pydantic import ValidationError\n",
    "\n",
    "# 間違ったデータ\n",
    "invalid_data = {\n",
    "    \"name\": \"Bob\",\n",
    "    \"age\": \"not a number\",\n",
    "    \"email\": \"bob@example.com\"\n",
    "}\n",
    "\n",
    "try:\n",
    "    user = User(**invalid_data)\n",
    "except ValidationError as e:\n",
    "    print(\"Validation Error:\")\n",
    "    print(e)\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "13daf9b9",
   "metadata": {},
   "source": [
    "## カスタムバリデータの例\n",
    "\n",
    "次に、`@validator`デコレータを使ったカスタムバリデータの例を試してみます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dde47d60",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "from pydantic import validator\n",
    "\n",
    "class UserWithCustomValidation(BaseModel):\n",
    "    name: str\n",
    "    age: int\n",
    "    email: str\n",
    "\n",
    "    @validator('email')\n",
    "    def validate_email(cls, value):\n",
    "        if not value.endswith(\"@example.com\"):\n",
    "            raise ValueError(\"Email must be on the 'example.com' domain.\")\n",
    "        return value\n",
    "\n",
    "# 正しいデータ\n",
    "user_valid = UserWithCustomValidation(name=\"Alice\", age=25, email=\"alice@example.com\")\n",
    "print(user_valid)\n",
    "\n",
    "# 間違ったデータ\n",
    "try:\n",
    "    user_invalid = UserWithCustomValidation(name=\"Bob\", age=30, email=\"bob@gmail.com\")\n",
    "except ValidationError as e:\n",
    "    print(\"Validation Error:\")\n",
    "    print(e)\n",
    "    "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "885c372e",
   "metadata": {},
   "source": [
    "## 入れ子モデルの例\n",
    "\n",
    "Pydanticでは入れ子モデルも簡単に扱えます。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "35275b4b",
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "from typing import Optional\n",
    "\n",
    "class Address(BaseModel):\n",
    "    city: str\n",
    "    zip_code: str\n",
    "\n",
    "class UserWithAddress(BaseModel):\n",
    "    name: str\n",
    "    age: int\n",
    "    address: Optional[Address] = None\n",
    "\n",
    "data_with_address = {\n",
    "    \"name\": \"Emily\",\n",
    "    \"age\": 28,\n",
    "    \"address\": {\n",
    "        \"city\": \"Tokyo\",\n",
    "        \"zip_code\": \"100-0001\"\n",
    "    }\n",
    "}\n",
    "\n",
    "user_with_address = UserWithAddress(**data_with_address)\n",
    "print(user_with_address)\n",
    "    "
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 5
}
43
39
6

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
43
39

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?