<!-- llms-explorer concept facts · https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/ · pack 2026-09-08 · ~7434 tokens -->

# Pydantic v2 Data Validation and Modeling

> > Reference file — part of the programming-languages hub. Created via /dr research (Pydantic v2 data validation and modeling).

Parent: [Programming Languages](https://llms-explorer.com/tree/programming-languages/) · 19 facets · 102 facts · page: https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/

## Overview

- <!-- hub-reference-banner --> > Reference file - part of the programming-languages hub. Created via /dr research (Pydantic v2 data validation and modeling). > Sibling topics in this family are reference files under the hubs (programming-languages, software-engineering-patterns) - not standalone > skills. Ignore any "use the X skill" / related_skills / SKIP pointers below that name a bare sibling > skill; load that topic's references/<name>.md from the owning hub (see the hub's "Cross-hub map"). > For general Python idioms, type hints, packaging, and async, see references/python-patterns.md in this hub. > For the TypeScript/JS analog (runtime schema validation), see the top-level zod-schema-validation skill. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#overview)
- --- name: pydantic-v2 description: > Pydantic v2 expert - runtime data validation and modeling in Python powered by the Rust pydantic-core. Covers BaseModel and field definitions (Field, Annotated constraints), the three validator modes (field_validator / model_validator, before/after/wrap/plain), strict vs lax coercion and ConfigDict, serialization (model_dump / model_dump_json, aliases, include/exclude, computed_field, RootModel), TypeAdapter for non-model types, discriminated (tagged) unions, pydantic-settings (BaseSettings, SettingsConfigDict, env/.env/secrets), ValidationError handling, and V1→V2 migration plus performance anti-patterns. TRIGGER: defining or validating Pydantic models; field_validator / model_validator; Annotated constraints; strict mode / type coercion questions; model_dump / serialization / aliases; TypeAdapter; discriminated unions; BaseSettings / config from env; migrating Pydantic V1 → V2; Pydantic validation performance tuning. SKIP: TypeScript/JS runtime validation - use zod-schema-validation; general Python idioms, type hints, packaging, async - use python-patterns.md; pytest/Hypothesis testing - use python-testing.md; API/REST design - use software-engineering-patterns. --- — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#overview)

## Overview

- Pydantic is the most widely used data-validation library for Python. It validates data at runtime against Python type hints and produces structured, user-friendly errors when data is invalid. Pydantic v2 (released mid-2023, stable and current through 2026) rewrote the validation/serialization engine in Rust as a separate package, pydantic-core (built with PyO3). The result is ~5–50× faster than v1 (≈17× on a typical mixed-field model), with the Python layer reduced to schema definition while the hot path runs in compiled Rust. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#overview-1)
- Three packages make up the ecosystem: — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#overview-1)
  - pydantic - the Python API (BaseModel, Field, validators, TypeAdapter). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#overview-1)
  - pydantic-core - the Rust validation/serialization engine (not used directly). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#overview-1)
  - pydantic-settings - BaseSettings for config from env vars, .env, secrets. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#overview-1)
- Use it when you need to parse untrusted input (API bodies, config, JSON, ORM rows) into typed Python objects with guarantees, and serialize them back out. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#overview-1)

## 1. BaseModel and field definitions

- Subclass BaseModel; annotate fields with type hints. Validation runs on construction and on the explicit model_validate* entry points. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#1-basemodel-and-field-definitions)
  - Validation entry points: User(**data), User.model_validate(dict_or_obj), User.model_validate_json(json_str_or_bytes). JSON parsing happens inside Rust in model_validate_json - faster than json.loads() then model_validate. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#1-basemodel-and-field-definitions)
  - Field(...) carries metadata/constraints: default, default_factory, alias / validation_alias / serialization_alias, ge/gt/le/lt, min_length/max_length, pattern, description, frozen, exclude. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#1-basemodel-and-field-definitions)
  - Prefer Annotated[type, Field(...)] over field: type = Field(...) for constraints. Constraints inside Annotated are compiled into the core schema and run in Rust (no Python call overhead). They also compose with list[...], dict[...], etc. (e.g. list[Annotated[int, Field(gt=0)]]). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#1-basemodel-and-field-definitions)
  - from_attributes=True (in model_config, replaces v1 orm_mode) lets model_validate read attributes off arbitrary objects (e.g. ORM rows). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#1-basemodel-and-field-definitions)

## 2. Validators — field, model, and the before/after/wrap modes

- Pydantic distinguishes validators (input → validated value) from serializers (value → output). Validators run in a defined order around the core (Rust) validation. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#2-validators-field-model-and-the-beforeafterwrap-modes)
- Modes (the most-confused part of Pydantic v2): — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#2-validators-field-model-and-the-beforeafterwrap-modes)
  - mode="before" - runs on raw input before core coercion. Receives whatever was passed (often a dict or str); use to reshape/normalize input. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#2-validators-field-model-and-the-beforeafterwrap-modes)
  - mode="after" - runs on the already-validated, typed value. Safest default for business rules; you get a real int/str/submodel, not raw input. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#2-validators-field-model-and-the-beforeafterwrap-modes)
  - mode="wrap" - most powerful: receives the value and a handler callable; you decide whether/when to call the inner validator and can transform around it. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#2-validators-field-model-and-the-beforeafterwrap-modes)
  - mode="plain" - terminates validation; your function fully replaces core validation for that field (no core coercion runs). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#2-validators-field-model-and-the-beforeafterwrap-modes)
- model_validator(mode="before") receives the raw input dict for the whole model; mode="after" receives self (return self). Raise ValueError or AssertionError inside a validator and Pydantic wraps it into a ValidationError. Validators can take an info: ValidationInfo param for info.data (already-validated siblings), info.context, info.field_name. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#2-validators-field-model-and-the-beforeafterwrap-modes)
- Reusable validators: attach a validator to a type once with Annotated[str, AfterValidator(func)] / BeforeValidator / WrapValidator / PlainValidator - cleaner than repeating @field_validator across models. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#2-validators-field-model-and-the-beforeafterwrap-modes)

## 3. Strict vs lax mode and ConfigDict

- By default Pydantic is lax: it coerces compatible types ("123" → 123, "true" → True). Strict mode disables coercion and requires exact types. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#3-strict-vs-lax-mode-and-configdict)
- Strictness is layered (most → least specific): per-call model_validate(..., strict=True) > field-level Field(strict=True) / Strict() annotation > model_config. Common ConfigDict keys: — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#3-strict-vs-lax-mode-and-configdict)
  - strict, frozen (immutable + hashable; replaces v1 allow_mutation), — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#3-strict-vs-lax-mode-and-configdict)
  - extra = "ignore" (default) / "forbid" / "allow", — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#3-strict-vs-lax-mode-and-configdict)
  - validate_assignment=True (re-validate on attribute set; off by default), — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#3-strict-vs-lax-mode-and-configdict)
  - from_attributes=True (ORM reads), populate_by_name=True (accept field name and alias on input; renamed validate_by_name in newer versions), — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#3-strict-vs-lax-mode-and-configdict)
  - str_strip_whitespace, use_enum_values, arbitrary_types_allowed, — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#3-strict-vs-lax-mode-and-configdict)
  - json_schema_extra, ser_json_timedelta, etc. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#3-strict-vs-lax-mode-and-configdict)
- model_config is a dict (ConfigDict(...)), not the v1 nested class Config. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#3-strict-vs-lax-mode-and-configdict)

## 4. Serialization — model_dump, JSON, aliases, computed fields

- Key options (apply to all three): include / exclude (sets or nested dicts), by_alias=True (use serialization_alias), exclude_unset (only fields explicitly set - great for PATCH semantics), exclude_defaults, exclude_none, round_trip=True, warnings="error", context=.... — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#4-serialization-model_dump-json-aliases-computed-fields)
  - Custom serializers: @field_serializer("foo", mode="plain"|"wrap") for one field; @model_serializer for the whole model; Annotated[T, PlainSerializer(...)] for reusable type-level serialization. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#4-serialization-model_dump-json-aliases-computed-fields)
  - @computed_field - expose a derived @property in the serialized output: — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#4-serialization-model_dump-json-aliases-computed-fields)
  - RootModel[T] - a model whose top level is not an object (e.g. RootModel[list[int]], RootModel[dict[str, User]]); replaces v1 __root__. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#4-serialization-model_dump-json-aliases-computed-fields)

## 5. TypeAdapter — validation/serialization without a BaseModel

- TypeAdapter brings Pydantic's machinery to any type - list[User], dict[str,int], TypedDict, dataclasses, unions - without wrapping it in a model. Build the adapter once (it compiles a core schema) and reuse it. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#5-typeadapter-validationserialization-without-a-basemodel)
- Use it for bulk validation of homogeneous collections (build the adapter at module scope, not per call) and for validating request/response bodies that aren't models. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#5-typeadapter-validationserialization-without-a-basemodel)

## 6. Discriminated (tagged) unions

- Add a discriminator so the core validator picks one union member by a tag field instead of trying each - faster, and produces one clean error instead of N. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#6-discriminated-tagged-unions)
- For tags that aren't a plain field, use a callable discriminator via Discriminator(func) - and handle both dict and model inputs inside it, since the callable also runs during serialization. Discriminated unions also emit cleaner OpenAPI/JSON-Schema. Non-discriminated unions use smart mode (best-match) by default; left-to-right is available but usually worse. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#6-discriminated-tagged-unions)

## 7. pydantic-settings — typed configuration

- BaseSettings populates fields from (priority high→low): init kwargs → env vars → .env file → secrets dir → field defaults. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#7-pydantic-settings-typed-configuration)
  - Nested config: env_nested_delimiter="__" maps APP_DB__HOST to db.host (double underscore avoids clashing with names that contain single underscores). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#7-pydantic-settings-typed-configuration)
  - Secrets: secrets_dir reads each file as one field's value (Docker/K8s secrets). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#7-pydantic-settings-typed-configuration)
  - Customize sources by overriding settings_customise_sources (e.g. add a YAML or vault source, reorder priority). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#7-pydantic-settings-typed-configuration)
  - Best practice: .env for local dev only, commit a .env.example without secrets, use real environment variables / secret stores in production. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#7-pydantic-settings-typed-configuration)

## Tools / Frameworks

- FastAPI - built on Pydantic; request/response models are Pydantic models. FastAPI ≥0.100 requires Pydantic v2. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#tools-frameworks)
- bump-pydantic - automated V1→V2 codemod (renames @validator→@field_validator, Config→model_config, .dict()→.model_dump(), etc.). Run it, then review diffs. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#tools-frameworks)
- datamodel-code-generator - generate Pydantic models from JSON Schema / OpenAPI. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#tools-frameworks)
- json_schema() / model_json_schema() - emit JSON Schema (draft 2020-12) for any model or TypeAdapter, including for discriminated unions. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#tools-frameworks)
- mypy / pyright - Pydantic ships a mypy plugin; v2 models type-check well with both checkers. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#tools-frameworks)

## Methodology — choosing the right tool

- Validating a whole object with named fields? → BaseModel. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#methodology-choosing-the-right-tool)
- Validating a bare collection / TypedDict / union, no model needed? → TypeAdapter. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#methodology-choosing-the-right-tool)
- Reshaping raw input before typing? → @field_validator(mode="before") or a model mode="before" validator. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#methodology-choosing-the-right-tool)
- Cross-field business rule on typed data? → @model_validator(mode="after"). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#methodology-choosing-the-right-tool)
- Same validation reused across models/types? → Annotated[T, AfterValidator(...)]. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#methodology-choosing-the-right-tool)
- A union you can tag? → discriminated union (Field(discriminator=...)). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#methodology-choosing-the-right-tool)
- App configuration? → BaseSettings from pydantic-settings. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#methodology-choosing-the-right-tool)
- Need exact types, no coercion (e.g. money, ids)? → strict=True (per-field or model). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#methodology-choosing-the-right-tool)

## Practical Patterns

- Parse, don't validate-then-pass-dicts: convert at the boundary (Model.model_validate_json(body)) and pass typed models inward. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#practical-patterns)
- PATCH/partial update: model_dump(exclude_unset=True) to send only fields the client actually set. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#practical-patterns)
- Aliases for external naming: Field(validation_alias="userId", serialization_alias="user_id"); set populate_by_name=True to also accept the Python field name on input. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#practical-patterns)
- Immutable value objects: model_config = ConfigDict(frozen=True) → hashable, usable as dict keys / in sets. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#practical-patterns)
- Bulk validation: build one module-level TypeAdapter(list[Model]) and call validate_python once on the whole batch rather than looping per item. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#practical-patterns)
- Context-aware validation: Model.model_validate(data, context={...}), read via info.context in validators (e.g. inject locale, feature flags). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#practical-patterns)

## Anti-Patterns (and the fix)

- Overusing @field_validator for simple bounds. A Python validator always runs in Python (function-call overhead, duplicates checks). → Use Annotated[int, Field(ge=0)] so the constraint runs in Rust. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#anti-patterns-and-the-fix)
- mode="before" when you wanted typed data. Before-validators get raw input (often a str/dict), causing AttributeError/type bugs. → Use mode="after" for rules on validated values. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#anti-patterns-and-the-fix)
- Building a TypeAdapter inside a hot loop / per request. It recompiles the core schema each time. → Construct once at module scope and reuse. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#anti-patterns-and-the-fix)
- json.loads() then model_validate(dict). → model_validate_json(raw) parses and validates in one Rust pass. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#anti-patterns-and-the-fix)
- v1 carry-overs: class Config (→ model_config = ConfigDict(...)), @validator (→ @field_validator), @root_validator (→ @model_validator), .dict() (→ .model_dump()), .json() (→ .model_dump_json()), parse_obj (→ model_validate), parse_raw (→ model_validate_json), from_orm/orm_mode (→ model_validate(obj) + from_attributes=True), allow_mutation=False (→ frozen=True), each_item=True (→ annotate the inner type). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#anti-patterns-and-the-fix)
- Mutable default shared across instances (tags: list = []). → Field(default_factory=list). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#anti-patterns-and-the-fix)
- Mixing Pydantic v1 and v2 models in one validation graph - they don't nest cleanly; migrate the whole graph (bump-pydantic), or use the pydantic.v1 shim deliberately. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#anti-patterns-and-the-fix)
- Expecting validate_assignment by default. It's off; set ConfigDict(validate_assignment=True) if you mutate after construction. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#anti-patterns-and-the-fix)

## Troubleshooting

- ValidationError - iterate exc.errors() for structured dicts (loc, msg, type, input); exc.json() / exc.error_count() for reporting. The type string (e.g. int_parsing, missing, string_too_short) is the stable machine key. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#troubleshooting)
- "Input should be a valid integer" under strict mode - you passed a string to a strict int; coerce upstream or drop strictness for that field. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#troubleshooting)
- Serialization warning "Expected X but got Y" - a field's runtime value doesn't match its declared type (common with Any/subclasses); set model_dump(serialize_as_any=True) for duck-typed/polymorphic output, or fix the type. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#troubleshooting)
- PydanticUndefinedAnnotation / forward refs - call Model.model_rebuild() after the referenced type is defined (self-referential or late-bound models). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#troubleshooting)
- Settings not picked up - check env_prefix, the env_nested_delimiter, and that .env is found (relative to CWD unless an absolute env_file path is given). — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#troubleshooting)
- extra fields silently dropped - default is "ignore"; use "forbid" to catch typos in input, "allow" to keep them. — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#troubleshooting)

## References

- Pydantic official docs - Validation (concepts, API): https://docs.pydantic.dev/latest/ — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#references)
- Pydantic - Migration Guide (V1 → V2): https://docs.pydantic.dev/latest/migration/ — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#references)
- Pydantic - Unions / discriminated unions: https://docs.pydantic.dev/latest/concepts/unions/ — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#references)
- Pydantic - Serialization: https://docs.pydantic.dev/latest/concepts/serialization/ — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#references)
- Pydantic - Strict mode: https://docs.pydantic.dev/latest/concepts/strict_mode/ — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#references)
- Pydantic - Performance: https://docs.pydantic.dev/latest/concepts/performance/ — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#references)
- pydantic-settings - Settings Management: https://docs.pydantic.dev/latest/concepts/pydantic_settings/ — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#references)
- Pydantic v2 announcement (architecture / Rust core): https://pydantic.dev/articles/pydantic-v2 — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#references)
- pydantic-core (Rust engine): https://github.com/pydantic/pydantic-core — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#references)
- bump-pydantic (V1→V2 codemod): https://github.com/pydantic/bump-pydantic — [source](https://llms-explorer.com/sources/mdb-context-hub/pydantic-v2/#references)

## Where this helps

- Validating and parsing request/response bodies in a FastAPI service, where Pydantic v2 models are the native contract layer. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Building a typed configuration loader that needs to merge init kwargs, environment variables, .env files, and Docker/K8s secrets in a defined priority order. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Migrating a large Pydantic v1 codebase to v2 and needing to know which patterns changed — validator decorators, the Config class, serialization methods. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Validating a bulk collection of homogeneous records, such as an API returning a JSON list of objects, without wrapping each one in a full BaseModel. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Project ideas

- Build a FastAPI service where every request/response boundary uses Model.model_validate_json(body) to parse-and-validate in one step, and downstream code only ever handles typed models, never raw dicts. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Implement a typed settings loader with pydantic-settings that reads from env vars with env_nested_delimiter="__" for nested config and a secrets_dir for containerized secrets. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Build a discriminated-union schema for a polymorphic API payload, such as different event types sharing one endpoint, using a tag field so the validator picks the right member directly instead of trying each union member in turn. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Write a migration script using bump-pydantic to automate the V1→V2 codemod (@validator→@field_validator, Config→model_config, .dict()→.model_dump()), then manually review the diff for edge cases the codemod can't infer. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Common mistakes

- Overusing @field_validator for simple numeric or length bounds, which always runs in Python and duplicates work the Rust core could do faster via Annotated[int, Field(ge=0)]. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Writing a mode="before" validator expecting typed data, when before-validators receive raw input, often a str or dict, and can throw AttributeError or type errors if treated as already-validated. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Constructing a TypeAdapter inside a hot loop or per-request instead of once at module scope, forcing the core schema to recompile on every call. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Validating input into dicts and passing dicts around the codebase instead of parsing at the boundary and passing typed models inward — undermining the type safety Pydantic is meant to provide. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Known issues

- Strict-mode type coercion errors ("Input should be a valid integer") are unforgiving of otherwise-valid loose input like numeric strings, requiring either an explicit upstream coercion step or a deliberate choice to drop strictness for that field. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- A serialization warning like "Expected X but got Y" appears whenever a field's runtime value doesn't match its declared type, which is common with Any or subclass fields and requires either serialize_as_any=True or a type fix. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- Discriminator callables run during both validation and serialization, so a callable discriminator must handle both dict and model inputs correctly or it can fail silently in one direction but not the other. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*
- FastAPI 0.100+ requires Pydantic v2, so a codebase still depending on v1-only third-party libraries can hit a hard compatibility wall when upgrading FastAPI. — [source](https://llms-explorer.com/tree/pydantic-v2-data-validation-and-modeling/) *(AI-suggested, synthesized from this pack's existing facts — not extracted from a source document.)*

## Context files

- [Pydantic v2 Data Validation and Modeling](https://llms-explorer.com/downloads/sources/mdb-context-hub/pydantic-v2.md)
