Skip to content

Commit

Permalink
Doc updates.
Browse files Browse the repository at this point in the history
  • Loading branch information
kschwab committed Aug 13, 2024
1 parent 01c1057 commit e0f3feb
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 6 deletions.
66 changes: 66 additions & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,72 @@ class Settings(BaseSettings):
So if you provide extra values in a dotenv file, whether they start with `env_prefix` or not,
a `ValidationError` will be raised.

## Parsing default objects

Pydantic settings uses copy-by-reference when merging default (`BaseModel`) objects from different sources. This ensures
the original object reference is maintained in the final object instantiation. However, due to an internal limitation,
it is not possible to partially update a nested sub model field in a default object when using copy-by-reference; the
entirety of the sub model must be provided.

This behavior can be overriden by setting the `default_objects_copy_by_value` flag to `True`, which will allow partial
updates to sub model fields. Note of course the original default object reference will not be retained.

```py
import os

from pydantic import BaseModel, ValidationError

from pydantic_settings import BaseSettings, SettingsConfigDict


class SubModel(BaseModel):
val: int = 0
flag: bool = False


ORIGINAL_OBJECT = SubModel()


class SettingsCopyByReference(BaseSettings):
model_config = SettingsConfigDict(env_nested_delimiter='__')

default_object: SubModel = ORIGINAL_OBJECT


class SettingsCopyByValue(BaseSettings):
model_config = SettingsConfigDict(
env_nested_delimiter='__', default_objects_copy_by_value=True
)

default_object: SubModel = ORIGINAL_OBJECT


by_ref = SettingsCopyByReference()
assert by_ref.default_object is ORIGINAL_OBJECT

# Apply a partial update to the default object using environment variables
os.environ['DEFAULT_OBJECT__FLAG'] = 'True'

try:
# Copy by reference will fail
SettingsCopyByReference()
except ValidationError as err:
print(err)
"""
1 validation error for SettingsCopyByReference
nested.val
Field required [type=missing, input_value={'flag': 'TRUE'}, input_type=dict]
For further information visit https://errors.pydantic.dev/2/v/missing
"""

# Copy by value will pass
by_val = SettingsCopyByValue()
assert by_val.default_object is not ORIGINAL_OBJECT

print(by_val.model_dump())
#> {'default_object': {'val': 0, 'flag': True}}
```

## Command Line Support

Pydantic settings provides integrated CLI support, making it easy to quickly define CLI applications using Pydantic
Expand Down
14 changes: 8 additions & 6 deletions pydantic_settings/sources.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,12 +262,14 @@ def __init__(self, settings_cls: type[BaseSettings], default_objects_copy_by_val
if default_objects_copy_by_value is not None
else self.config.get('default_objects_copy_by_value', False)
)
if self.default_objects_copy_by_value:
for field_name, field_info in settings_cls.model_fields.items():
if is_dataclass(type(field_info.default)):
self.defaults[_field_name_for_signature(field_name, field_info)] = asdict(field_info.default)
elif is_model_class(type(field_info.default)):
self.defaults[_field_name_for_signature(field_name, field_info)] = field_info.default.model_dump()
for field_name, field_info in settings_cls.model_fields.items():
if not self.default_objects_copy_by_value:
if is_dataclass(type(field_info.default)) or is_model_class(type(field_info.default)):
self.defaults[_field_name_for_signature(field_name, field_info)] = field_info.default
elif is_dataclass(type(field_info.default)):
self.defaults[_field_name_for_signature(field_name, field_info)] = asdict(field_info.default)
elif is_model_class(type(field_info.default)):
self.defaults[_field_name_for_signature(field_name, field_info)] = field_info.default.model_dump()

def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
# Nothing to do here. Only implement the return statement to make mypy happy
Expand Down

0 comments on commit e0f3feb

Please sign in to comment.