piotrekkr
piotrekkr

Reputation: 3201

Pydantic - change data in nested model before validation

I have this small example of nested object with pydantic.

from typing import Dict
from pydantic import BaseModel, Field, ValidationError

class UserType(BaseModel):
    name: str = Field(min_length=1)
    type: str = Field(min_length=1)

class AppConfig(BaseModel):
    key1: int = Field(gt=0)
    objects: Dict[str, UserType]

try:
    data = {
        "key1": 1,
        "objects": {
            "type1": {
                "name": "Name 2",
            },
            "type2": {
                "name": "Name 1"
            }
        }
    }
    c = AppConfig(**data)
    print(c.model_dump_json())
except ValidationError as e:
    print(e)

This obviously fail because type is not set in UserType model. My goal is to somehow set each UserType.type using associated key from objects dictionary before the actual validation. Something like:

Is this possible somehow with pydantic?

I know I could do all this before passing data to main model but I want to know if this is possible in pydantic model.

I also tried using model_post_init() method like this:

from typing import Dict
from pydantic import BaseModel, Field, ValidationError

class UserType(BaseModel):
    name: str = Field(min_length=1)
    type: str = Field(min_length=1)

class AppConfig(BaseModel):
    key1: int = Field(gt=0)
    objects: Dict[str, UserType]

    def model_post_init(self, __context) -> None:
        values = self.dict()
        for obj_type, obj in values["objects"].items():
            print(obj, obj_type)
            obj["type"] = obj_type

try:
    data = {
        "key1": 1,
        "objects": {
            "type1": {
                "name": "Name 2",
                #"type": "t"
            },
            "type2": {
                "name": "Name 1",
                #"type": "t"
            }
        }
    }
    c = AppConfig(**data)
    print(c.model_dump_json())
except ValidationError as e:
    print(e)

But this method is executed after validation and validation failed earlier. Also tried to set some dummy type values in data payload and then override it in model_post_init() but this did not work at all and model had only original dummy values for type.

Upvotes: 0

Views: 602

Answers (1)

Yurii Motov
Yurii Motov

Reputation: 2353

You can update objects data before validation using field_validator with mode=before for objects field:

from typing import Dict

from pydantic import BaseModel, Field, ValidationError, field_validator


class UserType(BaseModel):
    name: str = Field(min_length=1)
    type: str = Field(min_length=1)

class AppConfig(BaseModel):
    key1: int = Field(gt=0)
    objects: Dict[str, UserType]

    
    @field_validator("objects", mode="before")
    @classmethod
    def validate_objects(cls, objects):
        for obj_type, obj_dict in objects.items():
            obj_dict.update({"type": obj_type})
        return objects

try:
    data = {
        "key1": 1,
        "objects": {
            "type1": {
                "name": "Name 2",
            },
            "type2": {
                "name": "Name 1"
            }
        }
    }
    c = AppConfig.model_validate(data)
    print(c.model_dump_json())
except ValidationError as e:
    print(e)

Upvotes: 1

Related Questions