3

I'm using pydantic to model objects which are then being serialized to json and persisted in mongodb For better encapsulation, I want to some fields to be private but I still want them to be serialized to json when saving to mongodb, and then deserialized back from json when I fetch the object from the db

how can this be done?

Example Model:

class MyModel(BaseModel):
    public_field: str
    _creation_time: str

    def __init__(self, public_field: str):
        super().__init__(public_field=public_field,
                         _creation_time=str(datetime.now()))


model = MyModel(public_field='foo')
json_str = model.json()
print(json_str)

The output of this code is:

{"public_field": "foo"}

I would like it to be something like this:

{"public_field": "foo", "_creation_time": "2023-03-03 09:43:47.796720"}

and then also to be able to deserialize the above json back with the private field populated

1
  • Related thread, unfortunately there is no answer yet. A new feature called computed fields may be available in pydantic v2, and if you really only want to parse leading underscore from serialization this thread should answer that. An alternative approach to private fields. Commented Mar 5, 2023 at 5:09

1 Answer 1

1

Pydantic doesn't really like this having these private fields. By default it will just ignore the value and is very strict about what fields get set.

One way around this is to allow the field to be added as an Extra (although this will allow more than just this one field to be added).

The following works as you might expect:

class MyModel(BaseModel, extra=Extra.allow):
    public_field: str
    _creation_time: str

    def __init__(self, **kwargs):
        kwargs.setdefault("_creation_time", str(datetime.now()))
        super().__init__(**kwargs)
model = MyModel(public_field='foo')
print(model)
json_str = model.json()
print(json_str)
print(MyModel.parse_obj({"public_field": "foo", "_creation_time": "2023-03-05 00:08:21.722193"}))
public_field='foo' _creation_time='2023-03-05 00:12:35.554712'
{"public_field": "foo", "_creation_time": "2023-03-05 00:12:35.554712"}
public_field='foo' _creation_time='2023-03-05 00:08:21.722193'

Note that because the field itself is being ignored, setting Field(default_factory=...) will not work, so you're stuck with the more awkward __init__ method,

Sign up to request clarification or add additional context in comments.

2 Comments

so if I have multiple private fields it's all or nothing? I can't control which fields get serialized?
@NirBrachel You still could, but you would need to provide a custom json encoder to the class which does the filtering for you. Pydantic is best used for having 1:1 representations in code of what represents the serialized data. Once you start adding things like private fields, its going against that mindset.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.