Pydantic Field Validator Coercion

Normalize messy form input with Pydantic field validators, trimming strings, coercing digit strings, and rejecting malformed values early.

PythonForm Validation

Code

snippet
from typing import Annotated

from pydantic import BaseModel, Field, field_validator, model_validator


class SignupForm(BaseModel):
    email: Annotated[str, Field(min_length=5, max_length=120)]
    age: Annotated[int, Field(ge=18, le=120)]
    zipcode: str
    password: str
    password_confirm: str

    @field_validator('email', mode='before')
    @classmethod
    def normalize_email(cls, raw: object) -> object:
        if isinstance(raw, str):
            candidate = raw.strip().lower()
            if candidate.startswith('mailto:'):
                candidate = candidate.removeprefix('mailto:')
            return candidate
        return raw

    @field_validator('zipcode', mode='before')
    @classmethod
    def digits_only(cls, raw: object) -> object:
        if isinstance(raw, str) and raw.isdigit() and len(raw) == 5:
            return raw
        raise ValueError('zipcode must be exactly 5 digits')

    @model_validator(mode='after')
    def passwords_match(self) -> 'SignupForm':
        if self.password != self.password_confirm:
            raise ValueError('passwords do not match')
        return self


form = SignupForm(
    email='  MAILTO:[email protected] ',
    age='30',
    zipcode='02138',
    password='hunter2xx',
    password_confirm='hunter2xx',
)
print('normalized email:', form.email, 'age type:', type(form.age).__name__)