mrkeyoor.com_
Sat 08 Aug 21:00 UTC
PyPIUtilsupdated 08 Aug 2026

dacite

dacite turns dictionaries into typed Python dataclass instances. Its single main function walks nested dataclasses, collections, optionals, unions, generics, and forward references, then constructs the target objects while checking annotated types. A Config object can cast enums, run type-specific conversion hooks, translate field names, reject extra keys, or require an unambiguous union match. It is a construction helper, not a serializer, schema generator, or full input-validation framework.

Verdict

Excellent at one narrow job: constructing nested dataclasses from already trusted dictionaries. If the source is external or you also need schemas and serialization, choose a validation or conversion framework that owns the full boundary.

API stability5/5The library centers on one from_dict function and a seven-option Config dataclass. Version 1.9.2 keeps the documented behavior for nested models, unions, collections, generics, hooks, casts, strict modes, key conversion, and forward references. With no runtime dependencies or framework integration points, there are few external APIs that can force churn.
Docs4/5The README documents every Config field with runnable examples, lists each exception, explains optional and union behavior, includes a realistic comparison with marshmallow, and is unusually direct that dacite does not validate data. It lacks a separate versioned documentation site, deeper performance guidance, and prominent recipes for modern union syntax, discriminated payloads, or field-specific aliases.
Maintenance3/5The latest PyPI release is 1.9.2 from February 2025 and GitHub shows the last repository push in March 2025. The repository is not archived and GitHub reports sixty-seven open items including issues and pull requests. The code is small and mature, but teams should expect conservative maintenance rather than frequent releases or immediate adoption of new typing features.
Ecosystem4/5Dacite recorded 5,049,958 downloads in the latest measured week and has 2,042 GitHub stars, strong use for a single-purpose Python mapper. It works with standard dataclasses and typing constructs without owning the model class. Its ecosystem is intentionally limited: validation, schema generation, aliases, and serialization come from separate packages and do not share one integrated configuration.

Use it if

  • You already model internal data with standard-library dataclasses and need to hydrate nested objects from JSON-decoded dictionaries
  • You want a narrow dependency-free mapper rather than adopting a second model system such as Pydantic
  • Your conversions can be expressed with type hooks, simple casts, or one global key-conversion function
  • You want clear exceptions with nested field paths for missing values, wrong types, unexpected keys, and ambiguous unions
Skip it if

Setup reality

`pip install dacite` adds no runtime dependencies and version 1.9.2 supports Python 3.7 or newer. The basic call is genuinely small, but production behavior depends on Config choices. Extra input keys are ignored by default, so API payload typos can disappear unless `strict=True`. Type checking is on by default, yet that is still structural construction rather than business validation: no range, length, email, cross-field, or custom field constraint runs unless you perform it elsewhere. Optional fields omitted from input become None, while required fields raise MissingValueError and dataclass defaults remain available. Unions are tried in declaration order, which makes overlapping dataclasses order-dependent; turn on `strict_unions_match` if ambiguity should fail. Type hooks run by target type and can affect every matching field, while `cast` calls the declared type constructor, so both can quietly normalize bad source data if used broadly. The `convert_key` callable receives dataclass field names and must return the corresponding input key, a direction that is easy to reverse accidentally. Forward references may need an explicit name-to-type mapping. Dacite caches resolved type information with a default maximum size of 2,048; applications generating many dataclass types can change or clear that global cache. Keep raw payload validation, error translation, and serialization outside this package.

Patterns

Build a dataclass from a dictionaryconstruct-dataclass

from dataclasses import dataclass
from dacite import from_dict

@dataclass
class User:
    name: str
    age: int

user = from_dict(User, {'name': 'Ada', 'age': 37})

This checks annotated runtime types but does not validate business rules such as nonnegative age or nonempty name.

Construct nested dataclassesconstruct-nested-models

from dataclasses import dataclass
from dacite import from_dict

@dataclass
class Address:
    city: str

@dataclass
class User:
    name: str
    address: Address

user = from_dict(User, {'name': 'Ada', 'address': {'city': 'London'}})

Nested dictionaries are recursively converted only where the target annotation is a dataclass type.

Convert lists of nested dataclassesconstruct-collections

from dataclasses import dataclass
from dacite import from_dict

@dataclass
class Item:
    sku: str
    quantity: int

@dataclass
class Order:
    items: list[Item]

order = from_dict(Order, {'items': [{'sku': 'A1', 'quantity': 2}]})

Collection element annotations drive recursive conversion; an untyped list cannot tell dacite which object to create.

Let omitted optional fields become Noneuse-optional-fields

from dataclasses import dataclass
from dacite import from_dict

@dataclass
class Profile:
    handle: str
    bio: str | None

profile = from_dict(Profile, {'handle': 'ada'})
assert profile.bio is None

A missing non-optional field without a dataclass default raises MissingValueError.

Convert ISO timestamps with a type hookparse-datetime-hook

from dataclasses import dataclass
from datetime import datetime
from dacite import Config, from_dict

@dataclass
class Event:
    occurred_at: datetime

config = Config(type_hooks={datetime: datetime.fromisoformat})
event = from_dict(Event, {'occurred_at': '2026-08-08T12:30:00+00:00'}, config)

A type hook applies to every field with that target type. Catch ValueError from the hook separately from dacite's own errors.

Cast strings into enum memberscast-enum-values

from dataclasses import dataclass
from enum import Enum
from dacite import Config, from_dict

class State(Enum):
    OPEN = 'open'
    CLOSED = 'closed'

@dataclass
class Ticket:
    state: State

ticket = from_dict(Ticket, {'state': 'open'}, Config(cast=[Enum]))

Casting Enum as a base class affects every enum field. Invalid values raise from the enum constructor.

Fail on unexpected input keysreject-extra-keys

from dacite import Config, UnexpectedDataError, from_dict

try:
    user = from_dict(User, payload, Config(strict=True))
except UnexpectedDataError as error:
    raise ValueError(f'unexpected keys: {sorted(error.keys)}') from error

Strict mode is off by default, so misspelled or newly added payload keys are otherwise ignored.

Reject ambiguous union matchesrequire-unambiguous-union

from dataclasses import dataclass
from dacite import Config, from_dict

@dataclass
class Cat:
    name: str

@dataclass
class Dog:
    name: str

@dataclass
class Pet:
    animal: Cat | Dog

pet = from_dict(Pet, {'animal': {'name': 'Milo'}}, Config(strict_unions_match=True))

This example raises StrictUnionMatchError because both members fit. Dacite has no discriminator mechanism to choose one.

Map snake_case fields to camelCase keysconvert-input-keys

from dacite import Config, from_dict

def to_camel(field: str) -> str:
    first, *rest = field.split('_')
    return first + ''.join(part.title() for part in rest)

config = Config(convert_key=to_camel)
person = from_dict(Person, {'firstName': 'Ada', 'lastName': 'Lovelace'}, config)

convert_key receives each dataclass field name and returns the input dictionary key, not the reverse.

Supply a forward-reference namespaceresolve-forward-reference

from dataclasses import dataclass
from dacite import Config, from_dict

@dataclass
class Node:
    child: 'Leaf'

@dataclass
class Leaf:
    value: str

node = from_dict(Node, {'child': {'value': 'x'}}, Config(forward_references={'Leaf': Leaf}))

Unresolvable names raise ForwardReferenceError. Keep the mapping aligned when types move between modules.

Report the nested field that failedtranslate-field-errors

from dacite import DaciteFieldError, from_dict

try:
    order = from_dict(Order, payload)
except DaciteFieldError as error:
    raise ValueError(f'invalid payload at {error.field_path}: {error}') from error

DaciteFieldError covers field-aware failures, but UnexpectedDataError and ForwardReferenceError use different base paths and may need separate handling.

Control dacite's global type cachetune-type-cache

from dacite import clear_cache, get_cache_size, set_cache_size

print(get_cache_size())  # 2048
set_cache_size(4096)
try:
    run_dynamic_imports()
finally:
    clear_cache()

Cache sizing is process-global. Only change it when profiling shows many dynamically generated dataclass types or repeated resolution work.

Alternatives

PackageRegistryPick it when
cattrsPyPIYou need both structuring and unstructuring, generated converters, and broader attrs plus dataclass support
mashumaroPyPIYou want fast code-generated serialization and deserialization methods attached to dataclasses
pydanticPyPIYou need validation, rich errors, aliases, JSON Schema, and serialization as part of the model layer
dataclasses-jsonPyPIYou want JSON load and dump helpers directly on dataclasses and accept decorator-driven configuration