Skip to content

Pydantic Exporting Models

As well as accessing model attributes directly via their names (e.g. model.foobar), models can be converted and exported in a number of ways:

model.dict(...)⚑

This is the primary way of converting a model to a dictionary. Sub-models will be recursively converted to dictionaries.

Arguments:

  • include: Fields to include in the returned dictionary.
  • exclude: Fields to exclude from the returned dictionary.
  • by_alias: Whether field aliases should be used as keys in the returned dictionary; default False.
  • exclude_unset: Whether fields which were not explicitly set when creating the model should be excluded from the returned dictionary; default False.
  • exclude_defaults: Whether fields which are equal to their default values (whether set or otherwise) should be excluded from the returned dictionary; default False.
  • exclude_none: Whether fields which are equal to None should be excluded from the returned dictionary; default False.

Example:

from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: float
    foo: str
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

# returns a dictionary:
print(m.dict())
"""
{
    'banana': 3.14,
    'foo': 'hello',
    'bar': {'whatever': 123},
}
"""
print(m.dict(include={'foo', 'bar'}))
#> {'foo': 'hello', 'bar': {'whatever': 123}}
print(m.dict(exclude={'foo', 'bar'}))
#> {'banana': 3.14}

dict(model) and iteration⚑

pydantic models can also be converted to dictionaries using dict(model), and you can also iterate over a model's field using for field_name, value in model:. With this approach the raw field values are returned, so sub-models will not be converted to dictionaries.

Example:

from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: float
    foo: str
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

print(dict(m))
"""
{
    'banana': 3.14,
    'foo': 'hello',
    'bar': BarModel(
        whatever=123,
    ),
}
"""
for name, value in m:
    print(f'{name}: {value}')
    #> banana: 3.14
    #> foo: hello
    #> bar: whatever=123

model.copy(...)⚑

copy() allows models to be duplicated, which is particularly useful for immutable models.

Arguments:

  • include: Fields to include in the returned dictionary.
  • exclude: Fields to exclude from the returned dictionary.
  • update: A dictionary of values to change when creating the copied model
  • deep: whether to make a deep copy of the new model; default False

Example:

from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    banana: float
    foo: str
    bar: BarModel


m = FooBarModel(banana=3.14, foo='hello', bar={'whatever': 123})

print(m.copy(include={'foo', 'bar'}))
#> foo='hello' bar=BarModel(whatever=123)
print(m.copy(exclude={'foo', 'bar'}))
#> banana=3.14
print(m.copy(update={'banana': 0}))
#> banana=0 foo='hello' bar=BarModel(whatever=123)
print(id(m.bar), id(m.copy().bar))
#> 139868119420992 139868119420992
# normal copy gives the same object reference for `bar`
print(id(m.bar), id(m.copy(deep=True).bar))
#> 139868119420992 139868119423296
# deep copy gives a new object reference for `bar`

model.json(...)⚑

The .json() method will serialise a model to JSON. Typically, .json() in turn calls .dict() and serialises its result. (For models with a custom root type, after calling .dict(), only the value for the __root__ key is serialised).

Arguments:

  • include: Fields to include in the returned dictionary.
  • exclude: Fields to exclude from the returned dictionary.
  • by_alias: Whether field aliases should be used as keys in the returned dictionary; default False.
  • exclude_unset: Whether fields which were not set when creating the model and have their default values should be excluded from the returned dictionary; default False.
  • exclude_defaults: Whether fields which are equal to their default values (whether set or otherwise) should be excluded from the returned dictionary; default False.
  • exclude_none: Whether fields which are equal to None should be excluded from the returned dictionary; default False.
  • encoder: A custom encoder function passed to the default argument of json.dumps(); defaults to a custom encoder designed to take care of all common types.
  • **dumps_kwargs: Any other keyword arguments are passed to json.dumps(), e.g. indent.

pydantic can serialise many commonly used types to JSON (e.g. datetime, date or UUID) which would normally fail with a simple json.dumps(foobar).

from datetime import datetime
from pydantic import BaseModel


class BarModel(BaseModel):
    whatever: int


class FooBarModel(BaseModel):
    foo: datetime
    bar: BarModel


m = FooBarModel(foo=datetime(2032, 6, 1, 12, 13, 14), bar={'whatever': 123})
print(m.json())
#> {"foo": "2032-06-01T12:13:14", "bar": {"whatever": 123}}

Advanced include and exclude⚑

The dict, json, and copy methods support include and exclude arguments which can either be sets or dictionaries. This allows nested selection of which fields to export:

from pydantic import BaseModel, SecretStr


class User(BaseModel):
    id: int
    username: str
    password: SecretStr


class Transaction(BaseModel):
    id: str
    user: User
    value: int


t = Transaction(
    id='1234567890',
    user=User(
        id=42,
        username='JohnDoe',
        password='hashedpassword'
    ),
    value=9876543210,
)

# using a set:
print(t.dict(exclude={'user', 'value'}))
#> {'id': '1234567890'}

# using a dict:
print(t.dict(exclude={'user': {'username', 'password'}, 'value': ...}))
#> {'id': '1234567890', 'user': {'id': 42}}

print(t.dict(include={'id': ..., 'user': {'id'}}))
#> {'id': '1234567890', 'user': {'id': 42}}

The ellipsis (...) indicates that we want to exclude or include an entire key, just as if we included it in a set. Of course, the same can be done at any depth level.

Special care must be taken when including or excluding fields from a list or tuple of submodels or dictionaries. In this scenario, dict and related methods expect integer keys for element-wise inclusion or exclusion. To exclude a field from every member of a list or tuple, the dictionary key '__all__' can be used as follows:

import datetime
from typing import List

from pydantic import BaseModel, SecretStr


class Country(BaseModel):
    name: str
    phone_code: int


class Address(BaseModel):
    post_code: int
    country: Country


class CardDetails(BaseModel):
    number: SecretStr
    expires: datetime.date


class Hobby(BaseModel):
    name: str
    info: str


class User(BaseModel):
    first_name: str
    second_name: str
    address: Address
    card_details: CardDetails
    hobbies: List[Hobby]


user = User(
    first_name='John',
    second_name='Doe',
    address=Address(
        post_code=123456,
        country=Country(
            name='USA',
            phone_code=1
        )
    ),
    card_details=CardDetails(
        number=4212934504460000,
        expires=datetime.date(2020, 5, 1)
    ),
    hobbies=[
        Hobby(name='Programming', info='Writing code and stuff'),
        Hobby(name='Gaming', info='Hell Yeah!!!'),
    ],
)

exclude_keys = {
    'second_name': ...,
    'address': {'post_code': ..., 'country': {'phone_code'}},
    'card_details': ...,
    # You can exclude fields from specific members of a tuple/list by index:
    'hobbies': {-1: {'info'}},
}

include_keys = {
    'first_name': ...,
    'address': {'country': {'name'}},
    'hobbies': {0: ..., -1: {'name'}},
}

# would be the same as user.dict(exclude=exclude_keys) in this case:
print(user.dict(include=include_keys))
"""
{
    'first_name': 'John',
    'address': {'country': {'name': 'USA'}},
    'hobbies': [
        {
            'name': 'Programming',
            'info': 'Writing code and stuff',
        },
        {'name': 'Gaming'},
    ],
}
"""

# To exclude a field from all members of a nested list or tuple, use "__all__":
print(user.dict(exclude={'hobbies': {'__all__': {'info'}}}))
"""
{
    'first_name': 'John',
    'second_name': 'Doe',
    'address': {
        'post_code': 123456,
        'country': {'name': 'USA', 'phone_code': 1},
    },
    'card_details': {
        'number': SecretStr('**********'),
        'expires': datetime.date(2020, 5, 1),
    },
    'hobbies': [{'name': 'Programming'}, {'name': 'Gaming'}],
}
"""

The same holds for the json and copy methods.

References⚑