I have a model defined like this;
class SomeModel(BaseModel):
name: str
class SomeOtherModel(BaseModel):
name: int
class MyModel(BaseModel):
items: List[Union[SomeModel, SomeOtherModel]]
@validator("items", always=True)
def validate(cls, value):
by_type = list(filter(lambda v: isinstance(v, SomeModel), value))
if len(by_type) < 1:
raise ValueError("we need at least one SomeModel")
The submodels are unimportant, essentially I need a list of different sub-models, and the list must contain at least one of the first type. All well and good.
Elsewhere in my code I am referring to this model (context: for the purposes of saving user settings, should be irrelevant for this question).
class ComposerModels(BaseModel):
user: List[MyModel] = []
system: List[MyModel] = []
class ComposerSettings(BaseModel):
models: ComposerModels
class UserSettings(BaseModel):
composer: ComposerSettings
My program needs to be able to save new models into the UserSettings model, something like this;
my_model = MyModel(items=[SomeModel(name="a"), SomeOtherModel(name=1)])
user_settings = UserSettings(
composer=ComposerSettings(
models=ComposerModels(
user=[my_model]
)
)
)
However, this throws an error;
Traceback (most recent call last):
File ".../pydantic/shenanigans.py", line 38, in <module>
user=[my_model]
File "pydantic/main.py", line 342, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for ComposerModels
user -> 0
we need at least one SomeModel (type=value_error)
This is where it gets weird.
For some reason, when trying to instantiate UserSettings
, it goes through validation of the items
field inside MyModel
, but instead of having been passed a list of sub-models, as expected, somehow it’s getting a list of MyModel instances instead. This obviously fails validation, and raises the error above. However, if I comment out the validation code, it works. No error, and the user settings model contains the MyModel
we’d expect.
I can’t just disable that validation, I need it elsewhere in my program… Any ideas on what’s going on here? I’m stumped…
I’m running python 3.7 with pydantic 1.10 on centos 7. I can’t easily upgrade any versions, because my company doesn’t believe in devops, so if this is a known bug with pydantic at that version I’ll have to think of something else.
2
Answers
It must be some kind of bug with pydantic
v1.10
. No wonder they deprecatedvalidator
in thev2
.I have done a slight modification, so that it works. I think there will be no problem for you, in initializing the
UserSettings
class with empty user and system.Hope this is the output you have been expecting:
Use a different function name for the validator. The
validate
method is an existing method of theBaseModel
and you’re overriding it. Whenever a new object is created, pydantic validates the fields with their corresponding types usingvalidate
method of the model.Since you have overriden the
validate
method, it will be used to validate the model.The flow of the code will be as follows:
Now, to validate
user
it will first validatemy_model
withMyModel
It will call something similar to
MyModel.validate(my_model)
internally (you can check the exact source here https://github.com/pydantic/pydantic/blob/547925887071a8cedf4642c4eb16ed749bf802cc/pydantic/v1/main.py#L1074) and since you’ve overriden thevalidate
method theisinstance(v, SomeModel)
check will not be satisfied formy_model
and it will fail and raise exception..So the solution is simply to use different function name.
This will work as expected.