With Pydantic is it possible to build a model where the type of one of its fields is set via an argument when creating an instance?
For example:
class Animal(BaseModel):
name: str
class Dog(Animal):
# more custom fields...
class Cat(Animal):
# more custom fields...
class Person(BaseModel):
name: str
pet: Cat | Dog
person_args = {
"name": "Dan",
# set which type to use via some kind of argument?
"_pet_type": Dog,
"pet": {
"name": "Fido",
# ...
},
}
person = Person(**person_args)
I'm aware that typically you could use a discriminated union to resolve which type to use. However in this particular case the information for which type I should validate against exists outside of this set of data in another, related model.
Alternately could I use some kind of private field on the types being discriminated that I set but isn't included in validated output? Something like:
person_args = {
"name": "Dan",
"pet": {
"_pet_type": Dog, # exclude from output
"name": "Fido",
# ...
},
}
person = Person(**person_args)
I'd like to avoid use of a custom validator as I'm using FastAPI and I want the potential types to be reflected properly in the OpenAPI schema.
I'm currently on Pydantic v1.x, but would consider upgrading to v2 if it would help me solve this issue.
Consider doing something like this:
class PetType(str, Enum):
DOG = "dog"
CAT = "cat"
class Animal(BaseModel):
name: str
@classmethod
def from_dict(cls, animal: dict) -> "Animal":
"""Build animal based on dict provided."""
type_ = PetType(animal["type"])
pet_mapper: dict[PetType, Type[Animal]] = {
PetType.DOG: Dog,
PetType.CAT: Cat,
}
return pet_mapper[type_].model_validate(obj=animal)
class Dog(Animal):
pass
class Cat(Animal):
pass
class Person(BaseModel):
name: str
pet: Cat | Dog
@classmethod
def from_dict(cls, person: dict) -> "Person":
"""Build person based on dict provided."""
return cls(
name=person["name"],
pet=Animal.from_dict(animal=person["pet"])
)
person_args = {
"name": "Dan",
"pet": {
"type": "cat",
"name": "Fido",
},
}
person = Person.from_dict(person=person_args)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With