Complete worked example
python
import re
from pydantic import BaseModel, ValidationError, field_validator
class Slug(BaseModel):
value: str
@field_validator("value", mode="before")
@classmethod
def normalize(cls, value):
return value.strip().lower() if isinstance(value, str) else value
@field_validator("value", mode="after")
@classmethod
def grammar(cls, value):
if not re.fullmatch(r"[a-z][a-z0-9-]*", value):
raise ValueError("invalid slug")
return value
def inspect_example(payload):
try:
return {"value": Slug.model_validate(payload).value}
except ValidationError as error:
return {"error": sorted({e["type"] for e in error.errors()})}- Reasoning
- The before method checks isinstance(value, str) before calling string operations. The after method then receives a string, applies a full-match grammar and returns that value. A normalized " Backend " becomes backend; a value containing a plus sign fails the domain check. Each decorator names the field and the validation mode, and each class method returns the value needed by the next stage. The fixture distinguishes a field type error from a domain value_error. Those error types are intentionally different evidence: one input was not a string, while another was a string outside the allowed grammar.