4

I'm currently trying to automatically save a pydantic.BaseSettings-object to a json-file on change. The model is loaded out of the json-File beforehand. A minimal working example of the saving procedure is as follows:

import json
from pydantic import BaseModel, BaseSettings, root_validator
from typing import Any, Dict


class NestedSettings(BaseModel):
    test: str = 'foobar'


class Settings(BaseSettings):
    nested: NestedSettings = NestedSettings()
    foo: str = 'bar'

    @root_validator
    def save_settings(cls, values: Dict[str, Any]) -> Dict[str, Any]:
        print('SAVING!')

        def serialize_basemodel(model: BaseModel):
            if isinstance(model, BaseModel):
                return model.dict()
            else:
                raise TypeError(f'{type(model)} is not serializable!')

        with open('config.json', 'w', encoding='utf-8') as f:
            json.dump(values, f, ensure_ascii=False, indent=2,
                      default=serialize_basemodel)
        return values

    class Config:
        validate_assignment: bool = True


s = Settings()
print('Saving top level:')
s.foo = 'baz'
print('Saving bottom level:')
s.nested.test = 'bar'

The output generated is:

SAVING!
Saving top level:
SAVING!
Saving bottom level:

I'd like to let the object save even after assigning the bottom level s.nested.bar. Is there any way to achieve this?

Thanks in advance for any hints!

H. Müller
  • 109
  • 1
  • 9

1 Answers1

2

My answer isn't elegant, but since the validate_assignment seems to only check the the root fields you can still take advantage of the of the action by changing

s.nested.test

to

s.nested = {'test': 'bar'}

and it will work. Here is the example code and output for better demonstration.

class Settings(BaseSettings):
    nested: NestedSettings = NestedSettings()
    foo: str = 'bar'

    @root_validator(pre=False)
    def save_settings(cls, values: Dict[str, Any]) -> Dict[str, Any]:
        print('SAVING!')
        print(values)
        def serialize_basemodel(model: BaseModel):
            if isinstance(model, BaseModel):
                return model.dict()
            else:
                raise TypeError(f'{type(model)} is not serializable!')

        with open('config.json', 'w', encoding='utf-8') as f:
            json.dump(values, f, ensure_ascii=False, indent=2,
                      default=serialize_basemodel)
        return values

    class Config:
        validate_assignment: bool = True


s = Settings()
print('Saving top level:')
s.foo = 'baz'
print('Saving bottom level:')
s.nested = {'test': 'bar'}
SAVING!
{'nested': NestedSettings(test='foobar'), 'foo': 'bar'}
Saving top level:
SAVING!
{'nested': NestedSettings(test='foobar'), 'foo': 'baz'}
Saving bottom level:
SAVING!
{'nested': NestedSettings(test='bar'), 'foo': 'baz'}
Troy Sincomb
  • 46
  • 1
  • 4
  • Thank you really much! After much tinkering around I did not stumble across anything better. So I guess it will have to do. – H. Müller Apr 23 '22 at 07:49