How to validate more than one field of a Pydantic model?

To extend on the answer of Rahul R, this example shows in more detail how to use the pydantic validators.

This example contains all the necessary information to answer your question.

Note, that there is also the option to use a @root_validator, as mentioned by Kentgrav, see the example at the bottom of the post for more details.

import pydantic

class Parent(pydantic.BaseModel):
    name: str
    comments: str

class Customer(Parent):
    address: str
    phone: str

    # If you want to apply the Validator to the fields "name", "comments", "address", "phone"
    @pydantic.validator("name", "comments", "address", "phone")
    @classmethod
    def validate_all_fields_one_by_one(cls, field_value):
        # Do the validation instead of printing
        print(f"{cls}: Field value {field_value}")

        return field_value  # this is the value written to the class field

    # if you want to validate to content of "phone" using the other fields of the Parent and Child class
    @pydantic.validator("phone")
    @classmethod
    def validate_one_field_using_the_others(cls, field_value, values, field, config):
        parent_class_name = values["name"]
        parent_class_address = values["address"] # works because "address" is already validated once we validate "phone"
        # Do the validation instead of printing
        print(f"{field_value} is the {field.name} of {parent_class_name}")

        return field_value 

Customer(name="Peter", comments="Pydantic User", address="Home", phone="117")

Output

<class '__main__.Customer'>: Field value Peter
<class '__main__.Customer'>: Field value Pydantic User
<class '__main__.Customer'>: Field value Home
<class '__main__.Customer'>: Field value 117
117 is the phone number of Peter
Customer(name="Peter", comments="Pydantic User", address="Home", phone="117")

To answer your question in more detail:

Add the fields to validate to the @validator decorator directly above the validation function.

  • @validator("name") uses the field value of "name" (e.g. "Peter") as input to the validation function. All fields of the class and its parent classes can be added to the @validator decorator.
  • the validation function (validate_all_fields_one_by_one) then uses the field value as the second argument (field_value) for which to validate the input. The return value of the validation function is written to the class field. The signature of the validation function is def validate_something(cls, field_value) where the function and variable names can be chosen arbitrarily (but the first argument should be cls). According to Arjan (https://youtu.be/Vj-iU-8_xLs?t=329), also the @classmethod decorator should be added.

If the goal is to validate one field by using other (already validated) fields of the parent and child class, the full signature of the validation function is def validate_something(cls, field_value, values, field, config) (the argument names values,field and config must match) where the value of the fields can be accessed with the field name as key (e.g. values["comments"]).

Edit1: If you want to check only input values of a certain type, you could use the following structure:

@validator("*") # validates all fields
def validate_if_float(cls, value):
    if isinstance(value, float):
        # do validation here
    return value

Edit2: Easier way to validate all fields together using @root_validator:

import pydantic

class Parent(pydantic.BaseModel):
    name: str
    comments: str

class Customer(Parent):
    address: str
    phone: str

    @pydantic.root_validator()
    @classmethod
    def validate_all_fields_at_the_same_time(cls, field_values):
        # Do the validation instead of printing
        print(f"{cls}: Field values are: {field_values}")
        assert field_values["name"] != "invalid_name", f"Name `{field_values['name']}` not allowed."
        return field_values

Output:

Customer(name="valid_name", comments="", address="Street 7", phone="079")
<class '__main__.Customer'>: Field values are: {'name': 'valid_name', 'comments': '', 'address': 'Street 7', 'phone': '079'}
Customer(name="valid_name", comments="", address="Street 7", phone="079")
Customer(name="invalid_name", comments="", address="Street 7", phone="079")
ValidationError: 1 validation error for Customer
__root__
  Name `invalid_name` not allowed. (type=assertion_error)

Leave a Comment