Pydantic とは
Pydanticは、データのバリデーションや設定管理のプロセスを簡素化するPythonライブラリです。データモデルの定義や入力データのバリデーション、アプリケーションの設定管理を行うシンプルで直感的な方法を提供し、厳密な型付けとエラーハンドリングを維持します。
Pydanticは、FastAPIやStarletteなどの人気のあるWebフレームワークとシームレスに連携するように設計されていますが、データのバリデーションや設定管理が必要な任意のPythonアプリケーションで使用することができます。
Pydanticの主な利点の1つは、ボイラープレートコードを減らし、データのバリデーションをより宣言的にする能力です。開発者は、単純なクラス構文を使用してデータモデルを定義し、Pydanticがその他の作業、型チェック、バリデーション、エラーハンドリングなどを処理することができます。
Pydantic のインストール
Pydanticをインストールするには、次のコマンドを実行します。
$ pip install pydantic
Pydantic モデルの作成
Pydanticモデルの作成は、モデルのフィールドを定義し、必要な場合にバリデータを追加することで、簡単なプロセスです。
Pydanticモデルを作成する手順は次のとおりです。
- pydanticモジュールからBaseModelクラスをインポートします。
from pydantic import BaseModel
- BaseModelをサブクラス化し、フィールドを追加してモデルを定義します。
class User(BaseModel):
    id: int
    name: str
    email: str
この例では、id、name、emailの3つのフィールドを持つUserモデルを定義しています。各フィールドの型は、Pythonの注釈を使用して指定しています。
- 必要に応じて、フィールドにバリデータを追加します。Pydanticは、MinValue、MaxValue、Regexなど、入力データが特定の要件を満たすようにするために使用できる幅広い組み込みバリデータを提供しています。
from pydantic import EmailStr
class User(BaseModel):
    id: int
    name: str
    email: EmailStr
この更新された例では、emailフィールドをEmailStrフィールドに置き換え、入力データが有効な電子メールアドレスであることを自動的に検証しています。
- オプションで、typingモジュールからOptionalとRequiredクラスを使用して、必須のフィールドとオプションのフィールドを指定することができます。
from typing import Optional
class User(BaseModel):
    id: int
    name: str
    email: Optional[EmailStr]
この最後の例では、emailフィールドをOptionalクラスでラップしてオプションとし、emailフィールドが含まれていなくても入力データが有効であることを意味します。
これらのステップに従うことで、入力データを検証し、Pythonアプリケーションがスムーズに実行されるようにすることができるPydanticモデルを簡単に作成できます。
Pydantic モデルを使用したデータ検証
Pydanticは、データモデルを定義して使用する簡単な方法を提供することで、Pythonアプリケーションでデータを簡単に検証することができます。この記事では、Pydanticモデルを使用してデータを検証する方法について説明します。
入力データの検証
Pydanticを使用して入力データを検証するには、期待される入力データの構造を定義するPydanticモデルを作成するだけです。その後、このモデルを使用して、アプリケーションが受信した任意の入力データを検証できます。
次は、Pydanticを使用して入力データを検証する例です。
from pydantic import BaseModel
class User(BaseModel):
    id: int
    name: str
    email: str
def create_user(user_data: dict):
    user = User(**user_data)
    # The above line validates the input data and raises a ValueError if it's invalid
    # If the input data is valid, you can continue with creating the user
    # ...
この例では、3つのフィールド(id、name、およびemail)を持つUser Pydanticモデルを定義し、create_user関数を定義しています。この関数は、入力データの辞書を引数として受け取ります。
入力データを検証するために、**構文を使用してuser_data辞書を展開してUserモデルの新しいインスタンスを作成します。入力データが有効な場合、Userインスタンスは正常に作成されます。入力データが無効な場合、ValueErrorが発生します。
出力データの検証
入力データだけでなく、Pydanticを使用して出力データも検証できます。これは、アプリケーションが返すデータが特定の要件を満たしていることを確認したい場合に便利です。
以下は、Pydanticを使用して出力データを検証する例です。
from typing import List
from pydantic import BaseModel
class User(BaseModel):
    id: int
    name: str
    email: str
def get_users() -> List[User]:
    # Get a list of users from the database
    users_data = [
        {"id": 1, "name": "Alice", "email": "alice@example.com"},
        {"id": 2, "name": "Bob", "email": "bob@example.com"}
    ]
    # Convert the list of user data dictionaries to a list of User instances
    users = [User(**user_data) for user_data in users_data]
    # Validate the output data
    return users
この例では、ユーザーデータのリストを返すget_users関数を定義しています。データの辞書リストをリスト内包表記を使ってUserインスタンスのリストに変換します。
出力データを検証するには、単にUserインスタンスのリストを返すだけです。Pydanticは自動的に各Userインスタンスを検証し、無効なインスタンスがある場合は例外を発生させます。
組み込みバリデータ
Pydanticには、データモデルが特定の要件を満たしていることを確認するために使用できる多数の組み込みバリデータが用意されています。この記事では、もっとも一般的に使用されるバリデータのいくつかを説明し、使用方法の例を示します。
EmailStr
このバリデータは、文字列が有効なメールアドレスであることを保証します。
from pydantic import BaseModel, EmailStr
class User(BaseModel):
    name: str
    email: EmailStr
UrlStr
このバリデータは、文字列が有効なURLであることを保証します。
from pydantic import BaseModel, UrlStr
class Website(BaseModel):
    name: str
    url: UrlStr
Length
このバリデータは、文字列が最小長と/または最大長を持っていることを保証します。
from pydantic import BaseModel, constr, Length
class User(BaseModel):
    name: constr(min_length=1, max_length=50)
    bio: constr(max_length=500)
MinMaxValue
これらのバリデータは、数値が最小値以上または最大値以下であることを保証します。
from pydantic import BaseModel, conint, MinValue, MaxValue
class Product(BaseModel):
    name: str
    price: conint(gt=0, le=10000)
    quantity: conint(ge=0)
    discount: conint(ge=0, le=50)
Regex
このバリデータは、文字列が正規表現パターンに一致することを保証します。
from pydantic import BaseModel, constr, Regex
class User(BaseModel):
    name: constr(regex=r'^[a-zA-Z ]+$')
ConstrainedStr
このバリデータは、親クラスによって定義された制約を満たすように、文字列が保証されます。
from pydantic import BaseModel, ConstrainedStr
class Password(ConstrainedStr):
    min_length = 8
    max_length = 50
    regex = r'[a-zA-Z0-9_-]+'
class User(BaseModel):
    email: EmailStr
    password: Password
ConstrainedInt
このバリデータは、親クラスによって定義された制約を満たすように、整数が保証されます。
from pydantic import BaseModel, ConstrainedInt
class Age(ConstrainedInt):
    ge = 0
    le = 120
class User(BaseModel):
    name: str
    age: Age
Decimal
このバリデータは、数値が指定された小数点次の桁数を持つ小数であることを保証します。
from pydantic import BaseModel, condecimal
class Product(BaseModel):
    price: condecimal(ge=0, le=10000, max_digits=8, decimal_places=2)
Uuid
このバリデータは、文字列値が有効なUUIDであることを保証します。
from pydantic import BaseModel, UUID4
class Order(BaseModel):
    id: UUID4
IPv4Address
このバリデータは、文字列値が有効なIPv4アドレスであることを保証します。
from pydantic import BaseModel, IPv4Address
class NetworkInterface(BaseModel):
    ip_address: IPv4Address
DirectoryPath
このバリデータは、文字列値が有効なディレクトリパスであることを保証します。
from pydantic import BaseModel, DirectoryPath
class Config(BaseModel):
    data_dir: DirectoryPath
FilePath
このバリデータは、文字列値が有効なファイルパスであることを保証します。
from pydantic import BaseModel, FilePath
class Config(BaseModel):
    data_file: FilePath
Pydantic の高度な機能
Pydanticは、強力で柔軟なデータモデルを作成するためのいくつかの高度な機能を提供しています。以下はそのうちのいくつかの主要な機能です。
カスタムバリデータの作成
Pydanticは、フィールド用のカスタムバリデータを作成できるようにし、データのバリデーション方法を細かく制御することができます。独自のバリデータ関数を定義して、データモデルで使用できます。
from pydantic import BaseModel, validator
class User(BaseModel):
    name: str
    age: int
    @validator('age')
    def validate_age(cls, age):
        if age < 0 or age > 120:
            raise ValueError('Invalid age')
        return age
この例では、Userモデルのageフィールドに対して、0から120の範囲内かどうかをチェックするカスタムバリデータを定義しています。
2 つのフィールドのバリデーション
カスタムバリデータの一般的な用途の1つは、2つのフィールドを同時にバリデーションして、2つのフィールドの値が特定の条件や制約を満たすことを保証することです。
例えば、配送注文を表すPydanticモデルがあり、2つの列が配送先の住所を表している場合、street_addressとzip_codeです。郵便番号を住所に対応させるための郵便番号と住所のマッピングに基づいて、zip_codeが正しいstreet_addressに対応することを確認したい場合があります。
これを実現するために、street_addressとzip_codeの両方の列に対して、値の組み合わせが有効かどうかをチェックするカスタムバリデータを定義することができます。
from pydantic import BaseModel, validator
class ShippingOrder(BaseModel):
    street_address: str
    zip_code: str
    @validator('zip_code')
    def validate_zip_code(cls, value, values):
        # get the value of the street_address column from the values dictionary
        street_address = values.get('street_address')
        # perform validation logic
        if not is_valid_zip_code(street_address, value):
            raise ValueError('Invalid zip code for street address')
        return value
    @validator('street_address')
    def validate_street_address(cls, value, values):
        # get the value of the zip_code column from the values dictionary
        zip_code = values.get('zip_code')
        # perform validation logic
        if not is_valid_zip_code(value, zip_code):
            raise ValueError('Invalid street address for zip code')
        return value
def is_valid_zip_code(street_address, zip_code):
    # perform validation logic, e.g. using a mapping of zip codes to street addresses
    if zip_code == '12345' and street_address != '123 Main St.':
        return False
    return True
この例では、zip_code列とstreet_address列の2つのカスタムバリデータを定義しています。各バリデータは2つの引数を取ります。バリデーションされるフィールドの名前(zip_codeまたはstreet_address)とその値(value)、およびモデル内の全てのフィールドの値の辞書(values)です。
各バリデータは、実際のバリデーションロジックを実行する別個のis_valid_zip_code関数も呼び出します。この場合、is_valid_zip_code関数は、郵便番号を住所にマッピングしたものに基づいて、street_addressとzip_codeの組み合わせが有効かどうかを確認します。組み合わせが無効な場合、バリデータはエラーメッセージを伴うValueErrorを発生させます。
列挙型の使用
Pydanticは、特定の値のみを取ることができるフィールドを定義できる列挙型のビルトインサポートを提供しており、これによりデータモデルの正確性とセキュリティを確保できます。
from enum import Enum
from pydantic import BaseModel
class Gender(Enum):
    MALE = 'male'
    FEMALE = 'female'
class User(BaseModel):
    name: str
    gender: Gender
この例では、Genderの列挙型を定義し、Userモデルで使用して、genderフィールドがmaleまたはfemaleの値しか取らないようにします。
ネストされたモデルを扱う
Pydanticは、複雑なデータ構造を表現するために使用できるネストされたモデルを定義できます。
from pydantic import BaseModel
class Location(BaseModel):
    latitude: float
    longitude: float
class User(BaseModel):
    name: str
    location: Location
この例では、 Locationモデルを定義し、ユーザーの場所を表すためにUserモデル内のフィールドとして使用しています。
ORM ライブラリと Pydantic を使用する
PydanticはSQLAlchemyやTortoise-ORMなどの人気のあるORMライブラリと共に使用でき、データベースモデルとPydanticモデルの間で簡単に変換できます。
from sqlalchemy import Column, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from pydantic import BaseModel
Base = declarative_base()
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)
    age = Column(Integer)
class UserIn(BaseModel):
    name: str
    age: int
class UserOut(UserIn):
    id: int
    class Config:
        orm_mode = True
この例では、 UserのSQLAlchemyモデルを定義し、入力および出力用のPydanticモデルを定義します。 Configクラスのorm_mode=Trueは、PydanticにSQLAlchemyモデルをPydanticモデルに変換し、その逆を行うように指示します。
参考