Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Alter field after instantiation in Pydantic BaseModel class

With a Pydantic class as follows, I want to transform the foo field by applying a replace operation:

from typing import List
from pydantic import BaseModel

class MyModel(BaseModel):
    foo: List[str]

my_object = MyModel(foo="hello-there")
my_object.foo = [s.replace("-", "_") for s in my_object.foo]

How can I do the replace operation right within the class, when the object is created? Without Pydantic I would simply do that within __init(self, foo) but since Pydantic creates its own __init__ implementation I'm not sure how to proceed exactly.

like image 241
Jivan Avatar asked Sep 03 '25 15:09

Jivan


2 Answers

Using a pydantic BaseModel in version 2.0+

Here we have the __post_model_init__ dunder method at our disposal to work with the object after instantiation.

from typing import List
from pydantic import BaseModel

class MyModel(BaseModel):
    foo: List[str]

    def model_post_init(self, __context):
        self.foo = [s.replace("-", "_") for s in self.foo]

my_object = MyModel(foo=["hello-there"])

print(my_object)
# Outputs foo=['hello_there']

Using a pydantic BaseModel in version <2.0

It seems as you would have to override the basemodels init method, something like this:

from typing import List
from pydantic import BaseModel

class MyModel(BaseModel):
    foo: List[str]

    def __init__(self, **data):
        data["foo"] = [s.replace("-", "_") for s in data["foo"]]
        super().__init__(**data)

my_object = MyModel(foo=["hello-there"])

print(my_object)
# Outputs foo=['hello_there']

Using a Pydantic dataclass

... or you could also turn it into a pydantic dataclass and use the post init dunder provided by pydantic to do other things upon instantiation. e.g:

from typing import List
from pydantic.dataclasses import dataclass

@dataclass
class MyModel():
    foo: List[str]

    def __post_init__(self):
        self.foo = [s.replace("-", "_") for s in self.foo]

my_object = MyModel(foo=["hello-there"])

print(my_object)

# Outputs foo=['hello_there']

Comments on @validator and @field_validator

I would personally avoid using the @validator or @field_validator decorators to mutate the object purely based on their naming. Their namings suggests they are meant to be used for validating the object so I would not expect any mutation to happen if i saw it in code.

like image 158
tbjorch Avatar answered Sep 05 '25 14:09

tbjorch


Pydantic version 2.0+

According to the documentation, this is the syntax:

class MyModel(BaseModel):
    a: int
    b: str
    
    def model_post_init(self, __context: Any) -> None:
        # manipulation on fields
        self.a = 42
like image 25
Itamar Podjarski Avatar answered Sep 05 '25 16:09

Itamar Podjarski