How to invoke pydantic field_validator on another, optional field?

126 Views Asked by At

I am trying to use a field_validator to validate a field based on the presence of another, optional field. So this is really about validating one input based on another input (the classic "password1/password2" tutorial case, albeit validating password1 on optional password2).

Example: If a given plant='flower', then it must have a color (which is optional, because other plants may not have a color).

from typing import Optional
from pydantic import BaseModel, field_validator
from pydantic_core.core_schema import FieldValidationInfo


class MyClass(BaseModel):

    plant: str
    color: Optional[str] = None

    @field_validator('plant')
    def flowers_have_color(cls, v, info: FieldValidationInfo):
        if v == 'flower':
            if info.data['color'] is None:
                raise ValueError("if 'plant' is a flower, it must have a color")
        return v

Expected behavior:

>>> MyClass(plant='tree')                # ok
MyClass(plant='tree', color=None)

>>> MyClass(plant='tree', color='red')   # ok
MyClass(plant='tree', color='red')

>>> MyClass(plant='flower')              # raise

>>> MyClass(plant='flower', color='red') # ok
MyClass(plant='flower', color='red')

This raises the following error:

MyClass(plant='flower', color='red')
>> KeyError: 'color'

While the above code invokes the field_validator when passing a "flower" plant (as expected), it does not see the color inside the validator, because the color is Optional. Indeed we see

print(info.data.keys())
dict_keys([])

How can I make the optional field color available to the field_validator (in general, or at least if it is provided)?

EDIT: As per comment below (thanks to Yurii Motov) this can be solved by using a model_validator (see below). But seems like an overkill to me, given I really just want to validate a single field (no need for self, classmethods or objects, etc.), and anything needed for that validation is input to the class constructor. It feels weird that field_validator would not be able to work in this situation - and vice versa, if validating "any input against another input" were not to be possible with field_validator, then why have it in pydantic in the first place and not replace any and all field_validators by model_validators?

@model_validator(mode='after')
def plants_must_have_color(self):
    if self.plant == 'flower':
        if self.color is None:
            raise ValueError("'flower' must have a color")
    return self
0

There are 0 best solutions below