1

I'm trying to validate some nested dictionaries using Union, basically I have the following code:

from typing import Optional, Any, List, Union, Dict
from pydantic import BaseModel, Field
from devtools import debug

data = {
    "config": {
        "features": {
            "pre_emission": {
                "print_document": True
            },
            "post_emission": {
                "send_email": True,
                "send_push_notification": True,
                "branch_offices_related" : []
            },
        }
    }
}

class PreEmissionFeatures(BaseModel):
    print_document: Optional[bool] = Field(False)

class PostEmissionFeatures(BaseModel):
    send_email: Optional[bool] = Field(False)
    send_push_notification: Optional[bool] = Field(False)
    branch_offices_related: Optional[List[int]] = Field([])

class PreEmission(BaseModel):
    pre_emission: Optional[PreEmissionFeatures] = Field({})

class PostEmission(BaseModel):
    post_emission: Optional[PostEmissionFeatures] = Field({})

class SectorDocumentFeatures(BaseModel):
    features: Union[PreEmission, PostEmission] = Field({})

class Config(BaseModel):
    config: Dict[str, Union[str, SectorDocumentFeatures]] = Field({})


stuff = Config(config=data)
debug(stuff)

I'm expecting to have pre_emission and post_emission dictionaries as part of features dict, but the result is:

stuff: Config(
    config={
        'config': SectorDocumentFeatures(
            features=PreEmission(
                pre_emission=PreEmissionFeatures(
                    print_document=True,
                ),
            ),
        ),
    },
) (Config)

Looks like Union is not working, or what I'm missing here?

maudev
  • 974
  • 1
  • 14
  • 32

1 Answers1

1

According to the documentation Union[X, Y] means either X or Y. So your model will be populated with either pre_emission or post_emission. What you need is a common model combining those fields together, it might look like this:

from typing import Optional, List, Union, Dict
from pydantic import BaseModel

data = {
    "config": {
        "features": {
            "pre_emission": {
                "print_document": True
            },
            "post_emission": {
                "send_email": True,
                "send_push_notification": True,
                "branch_offices_related": []
            },
        }
    }
}


class PreEmissionFeatures(BaseModel):
    print_document: Optional[bool] = False


class PostEmissionFeatures(BaseModel):
    send_email: Optional[bool] = False
    send_push_notification: Optional[bool] = False
    branch_offices_related: Optional[List[int]] = []


class Emissions(BaseModel):
    pre_emission: Optional[PreEmissionFeatures] = {}
    post_emission: Optional[PostEmissionFeatures] = {}


class SectorDocumentFeatures(BaseModel):
    features: Emissions = {}


class Config(BaseModel):
    config: Dict[str, Union[str, SectorDocumentFeatures]] = {}


stuff = Config(config=data)
print(stuff)

Output:

{
    "config": {
        "config": {
            "features": {
                "post_emission": {
                    "branch_offices_related": [],
                    "send_email": True,
                    "send_push_notification": True,
                },
                "pre_emission": {"print_document": True},
            }
        }
    }
}

Also, take a look at this answer, you can use mutable default paraments in Pydantic.

funnydman
  • 9,083
  • 4
  • 40
  • 55