Alter field after instantiation in Pydantic BaseModel class
Question:
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.
Answers:
Using a pydantic BaseModel
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']
Use the @validator
decorator (in this case with the option each_item=True
, since we want the validator to look at each item in the list rather than the list itself):
from typing import List
from pydantic import BaseModel, validator
class MyModel(BaseModel):
foo: List[str]
@validator('foo', each_item=True)
def replace_hyphen(cls,v):
return v.replace("-","_")
my_object = MyModel(foo=["hello-there"])
print(my_object)
# Outputs foo=['hello_there']
or if you prefer dataclasses:
from typing import List
from pydantic import validator
from pydantic.dataclasses import dataclass
@dataclass
class MyModelDC():
foo: List[str]
@validator('foo', each_item=True)
def replace_hyphen(cls,v):
return v.replace("-","_")
my_objectDC = MyModelDC(foo=["hello-there"])
print(my_objectDC)
# Outputs foo=['hello_there']
Pydantic version 2.0+ (unreleased as of now)
You can do:
class MyModel(BaseModel):
a: int
b: str
def model_post_init(self, **kwargs) -> None:
values = self.dict()
# manipulation on fields
It is not yet documented but here is the test:
https://github.com/pydantic/pydantic/blob/594effa279668bd955e98f1cd5c036b37d3bbd40/tests/test_main.py#L1828-L1850
And here is the reference:
https://github.com/pydantic/pydantic/issues/1729#issuecomment-1300576214
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.
Using a pydantic BaseModel
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']
Use the @validator
decorator (in this case with the option each_item=True
, since we want the validator to look at each item in the list rather than the list itself):
from typing import List
from pydantic import BaseModel, validator
class MyModel(BaseModel):
foo: List[str]
@validator('foo', each_item=True)
def replace_hyphen(cls,v):
return v.replace("-","_")
my_object = MyModel(foo=["hello-there"])
print(my_object)
# Outputs foo=['hello_there']
or if you prefer dataclasses:
from typing import List
from pydantic import validator
from pydantic.dataclasses import dataclass
@dataclass
class MyModelDC():
foo: List[str]
@validator('foo', each_item=True)
def replace_hyphen(cls,v):
return v.replace("-","_")
my_objectDC = MyModelDC(foo=["hello-there"])
print(my_objectDC)
# Outputs foo=['hello_there']
Pydantic version 2.0+ (unreleased as of now)
You can do:
class MyModel(BaseModel):
a: int
b: str
def model_post_init(self, **kwargs) -> None:
values = self.dict()
# manipulation on fields
It is not yet documented but here is the test:
https://github.com/pydantic/pydantic/blob/594effa279668bd955e98f1cd5c036b37d3bbd40/tests/test_main.py#L1828-L1850
And here is the reference:
https://github.com/pydantic/pydantic/issues/1729#issuecomment-1300576214