## 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}