Skip to content

Commit f8483da

Browse files
committed
Doc updates.
1 parent 01c1057 commit f8483da

File tree

2 files changed

+74
-6
lines changed

2 files changed

+74
-6
lines changed

docs/index.md

+66
Original file line numberDiff line numberDiff line change
@@ -464,6 +464,72 @@ class Settings(BaseSettings):
464464
So if you provide extra values in a dotenv file, whether they start with `env_prefix` or not,
465465
a `ValidationError` will be raised.
466466

467+
## Parsing default objects
468+
469+
Pydantic settings uses copy-by-reference when merging default (`BaseModel`) objects from different sources. This ensures
470+
the original object reference is maintained in the final object instantiation. However, due to an internal limitation,
471+
it is not possible to partially update a nested sub model field in a default object when using copy-by-reference; the
472+
entirety of the sub model must be provided.
473+
474+
This behavior can be overriden by setting the `default_objects_copy_by_value` flag to `True`, which will allow partial
475+
updates to sub model fields. Note of course the original default object reference will not be retained.
476+
477+
```py
478+
import os
479+
480+
from pydantic import BaseModel, ValidationError
481+
482+
from pydantic_settings import BaseSettings, SettingsConfigDict
483+
484+
485+
class SubModel(BaseModel):
486+
val: int = 0
487+
flag: bool = False
488+
489+
490+
ORIGINAL_OBJECT = SubModel()
491+
492+
493+
class SettingsCopyByReference(BaseSettings):
494+
model_config = SettingsConfigDict(env_nested_delimiter='__')
495+
496+
default_object: SubModel = ORIGINAL_OBJECT
497+
498+
499+
class SettingsCopyByValue(BaseSettings):
500+
model_config = SettingsConfigDict(
501+
env_nested_delimiter='__', default_objects_copy_by_value=True
502+
)
503+
504+
default_object: SubModel = ORIGINAL_OBJECT
505+
506+
507+
by_ref = SettingsCopyByReference()
508+
assert by_ref.default_object is ORIGINAL_OBJECT
509+
510+
# Apply a partial update to the default object using environment variables
511+
os.environ['DEFAULT_OBJECT__FLAG'] = 'True'
512+
513+
try:
514+
# Copy by reference will fail
515+
SettingsCopyByReference()
516+
except ValidationError as err:
517+
print(err)
518+
"""
519+
1 validation error for SettingsCopyByReference
520+
nested.val
521+
Field required [type=missing, input_value={'flag': 'TRUE'}, input_type=dict]
522+
For further information visit https://errors.pydantic.dev/2/v/missing
523+
"""
524+
525+
# Whereas copy by value will pass
526+
by_val = SettingsCopyByValue()
527+
assert by_val.default_object is not ORIGINAL_OBJECT
528+
529+
print(by_val.model_dump())
530+
#> {'default_object': {'val': 0, 'flag': True}}
531+
```
532+
467533
## Command Line Support
468534

469535
Pydantic settings provides integrated CLI support, making it easy to quickly define CLI applications using Pydantic

pydantic_settings/sources.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -262,12 +262,14 @@ def __init__(self, settings_cls: type[BaseSettings], default_objects_copy_by_val
262262
if default_objects_copy_by_value is not None
263263
else self.config.get('default_objects_copy_by_value', False)
264264
)
265-
if self.default_objects_copy_by_value:
266-
for field_name, field_info in settings_cls.model_fields.items():
267-
if is_dataclass(type(field_info.default)):
268-
self.defaults[_field_name_for_signature(field_name, field_info)] = asdict(field_info.default)
269-
elif is_model_class(type(field_info.default)):
270-
self.defaults[_field_name_for_signature(field_name, field_info)] = field_info.default.model_dump()
265+
for field_name, field_info in settings_cls.model_fields.items():
266+
if not self.default_objects_copy_by_value:
267+
if is_dataclass(type(field_info.default)) or is_model_class(type(field_info.default)):
268+
self.defaults[_field_name_for_signature(field_name, field_info)] = field_info.default
269+
elif is_dataclass(type(field_info.default)):
270+
self.defaults[_field_name_for_signature(field_name, field_info)] = asdict(field_info.default)
271+
elif is_model_class(type(field_info.default)):
272+
self.defaults[_field_name_for_signature(field_name, field_info)] = field_info.default.model_dump()
271273

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

0 commit comments

Comments
 (0)