I am trying to add a validation in pydantic data model which is recursive. However, the validator is not raising an error even when the check fails.
from __future__ import annotations
from typing import Optional, Union, List
import orjson
from pydantic import BaseModel, validator
def orjson_dumps(v, *, default):
return orjson.dumps(v, default=default).decode()
class Condition(BaseModel):
operation: str
value: Union[List[Condition], Condition, float, int, list, str]
attribute_name: Optional[str]
@validator("operation", pre=True, always=True)
@classmethod
def validate_operations(cls, field_value):
if field_value not in ["and", "or", "equal"]:
msg = f"Supported operations: [and, or, equal]. Passed operation: {field_value}"
raise ValueError(msg)
return field_value
if __name__ == '__main__':
condition4 = {'operation': 'or', 'value': [
{'operation': 'equal', 'attribute_name': 'num_pay_txns_month', 'value': 6},
{'operation': 'incorrect_operation', 'attribute_name': 'num_pay_txns_week', 'value': 3}]}
print(Condition(**condition4))
Actual Output:
operation='or' value=[{'operation': 'equal', 'attribute_name': 'num_pay_txns_month', 'value': 6}, {'operation': 'incorrect_operation', 'attribute_name': 'num_pay_txns_week', 'value': 3}] attribute_name=None
Expected output:
ValueError
Ah, this took me a while to figure out. The problem is in the value
annotation. It is because you have a non-specified list
in that type union. Remove that and the validation error is raised as expected:
class Condition(BaseModel):
operation: str
value: Union[List[Condition], Condition, float, int, str]
...
The reason is that by adding the non-specified list
you are basically saying that value
is allowed to be list[Any]
. Thus, when all other attempts to validate that dictionary containing the wrong value fail, it is just taken "as is", i.e. some dictionary. Which is why value
on the top-level object is just a list of dictionaries, not of Condition
instances.