Skip to content
Aabha AI Academy

Stage 3 · L07

Understand Pydantic types, conversion and validation

Core · original Session 3

Pydantic validates data at a boundary and produces typed values. EquipmentIn accepts a name, integer quantity and Decimal daily_rate. quantity='3' converts to integer 3 in the default policy; quantity='three' cannot convert and raises validation error. Conversion is deliberate behavior, not proof that clients originally sent the correct JSON type.

Constraints are separate from types. An integer quantity of zero has the right Python type but violates ge=1. A rate of 12.505 violates decimal_places=2. An empty name violates min_length. extra='forbid' rejects hidden keys such as role. These request errors occur before the handler creates a database row; the baseline checks a fresh row count after every rejected payload.

Required and nullable describe different things. nickname: str | None permits null but remains required without a default. nickname: str | None = None permits omission too. The scoped test uses the first form and confirms that omission fails. For a future PATCH, model_dump(exclude_unset=True) preserves omission; an explicitly supplied null remains supplied and needs a domain decision.

Field(strict=True) on an integer rejects a numeric string that default conversion would accept. Use strictness where the contract needs it, and test that boundary. Strictness does not replace business rules, foreign keys or a transaction. Pydantic is not permission checking, email deliverability, stock enforcement or SQL injection protection.

Errors include locations and kinds so a client can identify the invalid field. Do not echo credential values or raw database exceptions in an API error. FastAPI maps request validation to 422 for these routes. A valid request can still encounter a uniqueness conflict at PostgreSQL; that later failure is 409 and must leave no partial write.

quantity='3' → integer 3; quantity=0 → range failure; rate='12.505' → precision failure; extra role → forbidden field; Invalid input → 422, no INSERT; Strict integer rejects '3'.; Nullable does not imply optional.
quantity='3' → integer 3; quantity=0 → range failure; rate='12.505' → precision failure; extra role → forbidden field; Invalid input → 422, no INSERT; Strict integer rejects '3'.; Nullable does not imply optional.

Follow the running code

Focused lesson example; see the end-of-stage capstone for the cumulative app · stage 03

from pydantic import BaseModel, ConfigDict, Field
class QuantityIn(BaseModel):
    model_config = ConfigDict(extra='forbid')
    quantity: int = Field(ge=1)
    note: str | None  # Nullable, but still required without a default.
valid = QuantityIn(quantity='2', note=None)
assert valid.quantity == 2

Predict and observe this focused example using the concepts explained above. Its boundary is stated in the focused answer.

Guided lab

  1. Read the explanation and predict the focused example’s outcome.
  2. Predict quantity='2', quantity=0, missing note and an extra role field. How would strict=True change numeric-string acceptance?
  3. Compare the observed outcome with the focused answer and state its boundary.

Expected: The non-strict integer accepts '2'; zero violates the bound; note may be None but omission is invalid because it has no default; role is an extra forbidden key. Field(strict=True) rejects the numeric string. None of these schema checks reserves stock or commits a database row.

  • Validation succeeds before a uniqueness conflict; it cannot guarantee a later database commit.

Focused exercise and answer

Complete this focused exercise before reading its answer. The full native transfer is introduced only at the end of the stage.

Your transfer task: Predict quantity='2', quantity=0, missing note and an extra role field. How would strict=True change numeric-string acceptance?

  1. Predict quantity='2', quantity=0, missing note and an extra role field. How would strict=True change numeric-string acceptance?
Inspect the matching answer

This answer addresses the focused exercise above; the cumulative implementation is shown only after the stage prerequisites.

The non-strict integer accepts '2'; zero violates the bound; note may be None but omission is invalid because it has no default; role is an extra forbidden key. Field(strict=True) rejects the numeric string. None of these schema checks reserves stock or commits a database row.

Check your reasoning

Does str | None mean a field may be omitted?

Show the explanation

Only if a default is supplied. Nullable permits null; required controls presence.

Reading progress

54 lessons remain open to guests. Marking a lesson read records reading only; it does not award assessment credit or a certificate.

Device reading marks require browser storage. Reading is always available.

Sign in or create an account to save separate account progress. Your current page is kept.