Dumping models to dicts and JSON

Pydantic · Essentials · 8 min

Every API response, cache entry and log line needs the model as plain data again.

Serialization is the reverse of validation. Two methods cover almost every case:

  • model_dump() returns a dict, recursing into nested models. By default it keeps Python objects such as datetime and UUID.
  • model_dump(mode="json") returns a dict with only JSON-compatible values (strings for dates, lists for sets).
  • model_dump_json() returns a JSON string directly from the Rust core; pass indent=2 for pretty output.

Filtering options shared by both:

  • include={"id", "name"} or exclude={"password"}; nested sets or dicts select inside sub-models
  • exclude_none=True drops fields whose value is None
  • exclude_defaults=True drops fields still equal to their default
  • exclude_unset=True drops fields the caller never provided, which is exactly what a PATCH endpoint needs: only apply what the client sent

model_copy(update={...}) makes a copy with some fields changed. Note that it does not validate the update; to get validation, dump, change and call model_validate again.

model_fields_set tells you which fields were explicitly set, the information behind exclude_unset.

The v1 methods .dict(), .json() and .copy() still work with deprecation warnings; use the model_ versions in new code.

Example

from datetime import datetime
from pydantic import BaseModel

class Profile(BaseModel):
    name: str
    bio: str | None = None
    theme: str = "light"
    joined: datetime

p = Profile(name="Ann", joined=datetime(2024, 1, 2, 3, 4), theme="dark")
print(p.model_dump())
# {'name': 'Ann', 'bio': None, 'theme': 'dark', 'joined': datetime.datetime(2024, 1, 2, 3, 4)}
print(p.model_dump(mode="json", exclude_none=True))
# {'name': 'Ann', 'theme': 'dark', 'joined': '2024-01-02T03:04:00'}
patch = Profile.model_validate({"name": "Ann", "joined": "2024-01-02T03:04"})
print(patch.model_dump(exclude_unset=True, include={"name", "theme"}))  # {'name': 'Ann'}
print(p.model_copy(update={"bio": "hi"}).bio)                            # hi

Remember

  • model_dump() -> dict, model_dump(mode="json") -> JSON-safe dict, model_dump_json() -> str
  • exclude_unset keeps only what the caller provided: ideal for PATCH
  • model_copy(update=...) skips validation

Watch out for

  • Passing model_dump() output with datetimes to json.dumps; use mode="json" or model_dump_json().

Check yourself

  1. Which call returns only the fields a client actually sent?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.pydantic.dev/latest/concepts/serialization/