Back to Blog
Python

Python Pydantic model_dump and model_validate Explained

python pydantic model_dump and model_validate: Learn how Pydantic's model_dump and model_validate work together to handle data validation and serialization in Python a...

pydanticpythonmodel-validationdata-serializationpydantic-v2
Illustration showing Pydantic model_validate converting external data into a typed model and model_dump converting it back to a dictionary

Pydantic v2 centers on two methods that sit on opposite sides of the same boundary: model_validate converts external data into a typed model instance, and model_dump converts a model instance back into a plain dictionary. Understanding python pydantic model_dump and model_validate together matters because they form the input and output edges of most Pydantic-based services.

from pydantic import BaseModel class User(BaseModel): id: int name: str # External data -> model instance user = User.model_validate({"id": 1, "name": "Ada"}) # Model instance -> plain dict data = user.model_dump() print(data) # {'id': 1, 'name': 'Ada'}

The two operations are not exact inverses. model_validate applies type coercion and validation rules; model_dump performs no validation and simply serializes the current field values. This asymmetry matters when you build APIs, background jobs, or data pipelines that pass Pydantic models across boundaries.

What model_validate Does During Construction

model_validate is a class method. It takes a dictionary, another model instance, or an object that exposes matching attributes, and constructs a validated instance of the model.

class Product(BaseModel): sku: str price: float product = Product.model_validate({"sku": "A-100", "price": 19.99})

Validation is the key behavior. Pydantic checks each field against its declared type, applies coercion where allowed (for example, "19.99" to 19.99), and raises a ValidationError when a value cannot be converted. This is the same validation that runs when you call the constructor directly:

product = Product(sku="A-100", price="19.99") # price coerced to float

The difference is that model_validate is explicit about the input format and is the recommended way to construct a model from data you did not create yourself, such as a JSON request body or a database row.

What model_dump Returns and Why It Matters

model_dump is an instance method. It returns a dictionary with the model's field names as keys and the current field values as values.

class Order(BaseModel): order_id: int items: list[str] order = Order(order_id=42, items=["laptop", "mouse"]) data = order.model_dump() # {'order_id': 42, 'items': ['laptop', 'mouse']}

For nested models, model_dump recursively converts every nested model into a dictionary. This makes it straightforward to hand a Pydantic model to code that expects plain Python data, such as a JSON serializer, a message queue, or a logging library.

class Customer(BaseModel): name: str class Invoice(BaseModel): number: int customer: Customer invoice = Invoice(number=1001, customer=Customer(name="Ada")) print(invoice.model_dump()) # {'number': 1001, 'customer': {'name': 'Ada'}}

model_dump does not mutate the model. It produces a new dictionary each time, so you can safely modify the result without affecting the original instance.

Key Differences Between the Two Methods

The table below summarizes the most important distinctions. These differences determine when each method is appropriate.

Aspectmodel_validatemodel_dump
Method typeClass methodInstance method
DirectionExternal data → modelModel → plain data
ValidationPerforms full validationNo validation
InputDict, object, or model instanceModel instance
OutputModel instanceDictionary
Common useParsing requests, DB rowsSerialization, logging, message payloads

Use model_validate when data crosses a trust boundary and must be checked. Use model_dump when you need a plain representation of data that is already known to be valid.

If you are migrating from Pydantic v1, model_validate replaces parse_obj, and model_dump replaces the old dict() method. The v2 names are more explicit about what each method does, and the old names were deprecated.

Using model_dump with include and exclude

model_dump accepts parameters that control which fields appear in the output. The include and exclude arguments accept sets, dicts, or lists of field names.

class User(BaseModel): id: int username: str email: str password_hash: str user = User(id=1, username="ada", email="ada@example.com", password_hash="...") public_data = user.model_dump(exclude={"password_hash"}) # {'id': 1, 'username': 'ada', 'email': 'ada@example.com'}

This is a common pattern when returning a model from an API endpoint. You can keep the full model internally but strip sensitive fields at the serialization boundary without creating a separate response schema.

Nested exclusion works as well. You can pass a nested dict to exclude to remove fields from nested models:

data = invoice.model_dump(exclude={"customer": {"name"}}) # {'number': 1001, 'customer': {}}

The same mechanism works with include, which restricts the output to only the listed fields. Both parameters can be combined, though using both at once can make the intent harder to read.

Using model_validate with from_attributes

By default, model_validate expects a dictionary. When you pass an arbitrary object, Pydantic raises a validation error unless you set from_attributes=True.

class DatabaseUser: def __init__(self, id: int, name: str): self.id = id self.name = name db_user = DatabaseUser(1, "Ada") user = User.model_validate(db_user, from_attributes=True)

This is useful when you work with ORM objects, dataclasses, or any object whose attributes match your model's field names. The from_attributes flag can also be set at the model level with model_config = ConfigDict(from_attributes=True), which avoids repeating the flag at every call site.

from pydantic import BaseModel, ConfigDict class User(BaseModel): model_config = ConfigDict(from_attributes=True) id: int name: str user = User.model_validate(db_user) # no flag needed

When from_attributes is enabled, Pydantic reads attributes directly from the source object instead of looking for dictionary keys. This keeps the validation path consistent whether the source is a dict or an object.

Performance and Operational Considerations

Validation is not free. model_validate runs type checks and coercion on every field, which adds CPU cost compared to directly assigning values. For most services this cost is negligible, but in high-throughput paths—such as processing thousands of messages per second—the overhead can become measurable.

model_dump is cheaper because it only reads field values and builds a dictionary. It does not re-validate anything. If you already have a validated model, dumping it repeatedly is a lightweight operation.

A practical pattern is to validate once at the boundary and then pass the validated model through the rest of the codebase. Avoid calling model_validate on data that has already been validated unless the data genuinely changed or came from an untrusted source.

Another operational concern is the distinction between model_dump and model_dump_json. The former returns Python objects, which may include non-serializable types like datetime or UUID. The latter returns a JSON string and handles those types automatically. Choosing the wrong one can cause failures when you pass the result to a JSON encoder.

Common Mistakes and How to Avoid Them

One common mistake is using model_dump on a model that contains fields with custom types that are not JSON-serializable. model_dump returns the raw Python values, so a datetime field stays a datetime object. If you need JSON-compatible output, use model_dump_json instead.

from datetime import datetime class Event(BaseModel): occurred_at: datetime event = Event(occurred_at=datetime.now()) print(event.model_dump()) # {'occurred_at': datetime.datetime(...)} print(event.model_dump_json()) # '{"occurred_at":"2024-..."}'

Another mistake is expecting model_validate to accept keyword arguments like a constructor. It does not; it takes a single positional argument that is the data to validate. If you need to pass fields directly, use the constructor or model_construct when you want to skip validation entirely.

A third issue arises when you call model_validate on data that is already a model instance. Pydantic will treat the instance as an object and attempt to read attributes from it. If the source model has the same field names, this works, but it is usually clearer to pass the model's model_dump() output or to rely on the fact that model_validate accepts another model instance directly.

When to Choose Each Method

Use model_validate when:

  • Parsing request bodies, query parameters, or external API responses
  • Loading rows from a database or message queue
  • Re-validating data that may have been mutated outside the model

Use model_dump when:

  • Returning data from an API endpoint
  • Passing a model to a library that expects plain dictionaries
  • Logging or debugging model state
  • Storing model data in a cache or queue

The two methods complement each other. A typical flow validates incoming data with model_validate, processes the model, and serializes the result with model_dump. Keeping these operations explicit makes the data flow of your application easier to trace and test, and it ensures that validation happens exactly where you intend it to.

python pydantic model_dump and model_validate: Practical Usa | RYUSLOG DEV