17

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.

Jivan
  • 21,522
  • 15
  • 80
  • 131

3 Answers3

26

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']
tbjorch
  • 1,544
  • 1
  • 8
  • 21
13

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']
Sacha
  • 785
  • 6
  • 7
  • really nice. The `each_item=True` bit sounds a bit too "dark magic" but still quite handy. – Jivan May 19 '21 at 11:53
8

Pydantic version 2.0+

You can do:

class MyModel(BaseModel):
    a: int
    b: str
    
    def model_post_init(self, __context) -> None:
        values = self.dict()
        # manipulation on fields

Documentation

Here is an example from the testcases: https://github.com/pydantic/pydantic/blob/main/tests/test_main.py#L1881-L1915

Ic3fr0g
  • 1,199
  • 15
  • 26