Annotations

dataclass-settings loaders evaluated in the order they’re found on the annotation of each field in a supported dataclass-like class.

No Annotation

A field without an annotation is ignored. Such fields must have a class-level default, or otherwise not be required in the class constructor.

from pydantic import BaseModel

class Example(BaseModel):
    foo: str = "foo"

Annotated field

A field annotated with typing.Annotated or typing_extensions.Annotated will be searched for compatible Loader objects.

Each identified loader will be evaluated in the order they’re defined for that field. The first non-None value returned by a loader will end the chain and that value will be used.

If no loader returns a non-None value, that field will not be sent to the class’s constructor.

As such, every field that should be allowed to be omitted should have a class-level default, or otherwise not be required in the class constructor.

from typing import Annotated
from pydantic import BaseModel
from dataclass_settings import Env, Secret

class Example(BaseModel):
    foo: Annotated[str, Env('FOO'), Secret('foo')] = "foo"

Note

You can use as many Loaders as you’d like, even repeating the same loader more than once!

Nested Classes

A field with a type annotation of a class that is a supported kind of class will be recursed into, and evaluated similarly to the root class.

from __future__ import annotations
from typing import Annotated
from pydantic import BaseModel
from dataclass_settings import Env, Secret

class Example(BaseModel):
    sub_example: SubExample


class SubExample(BaseModel):
    foo: Annotated[str, Env('FOO')] = "foo"

Note

“Optional class fields” are specifically handled to avoid requiring fields where they’re clearly not intended to be required.

For example:

class Example(BaseModel):
    sub_example: SubExample | None = None

In this case, if SubExample cannot be loaded, then it will be omitted, and resolved as None due to the class-level None default.