## Setup ```zsh uv add pydantic ``` If you plan to validate emails and time zones, too: ```zsh uv add 'pydantic[email,timezone]' ``` ## Model definition ```python from datetime import date from pydantic import BaseModel, Field, EmailStr, ValidationError from typing import Optional class UserInput(BaseModel): name: str email: EmailStr query: str order_id: Optional[int] = Field( None, description="5-digit order number (cannot start with 0)", ge=10000, le=99999 ) purchase_date: Optional[date] = None ``` Pydantic supports a large collection of [basic types](https://docs.pydantic.dev/latest/api/types/) as well as specialized [network types](https://docs.pydantic.dev/latest/api/networks/) incl. stuff such as the above [EmailStr](https://docs.pydantic.dev/latest/api/networks/#pydantic.networks.EmailStr). ## Raw JSON validation ```python json_str = '''{ name: "Alice", email: "[email protected]", query: "what is this?" }''' try: validated_input = UserInput.model_validate_json(json_str) except ValidationError as e: print(e) # ... Pydantic Validation error ... except ValueError as e: print(e) # ... JSON deserializaion error ... ``` ## Custom Field validation ```python class UserInput(BaseModel): name: str = Field(..., description="User's name") email: EmailStr = Field(..., description="User's email address") query: str = Field(..., description="User's query") order_id: Optional[str] = Field( None, description="Order ID if available (format: ABC-12345)" ) # Validate order_id format (e.g., ABC-12345) @field_validator("order_id") def validate_order_id(cls, order_id): if order_id is None: return order_id pattern = r"^[A-Z]{3}-\d{5}quot; if not re.match(pattern, order_id): raise ValueError( "order_id must be in format ABC-12345 " "(3 uppercase letters, dash, 5 digits)" ) return order_id ```