Wednesday 27 October 2021

Pydantic: Working with validators

Pydantic provides @validaor decorator to add custom validations to a model.

 

Let’s see it with an example.

class Employee(BaseModel):
    id: int
    name: str
    age: int

    @validator('name')
    def nameValidator(cls, value):
        if(len(value)< 5):
            raise ValueError('name must contain atleast 5 characters')
        return value

 

In the above example, I had written a validator for 'name' property, it throws value error when the name field contains less than 5 characters.

 

validator_demo_1.py

 

from pydantic import BaseModel, ValidationError, validator
from typing import Optional

class Employee(BaseModel):
    id: int
    name: str
    age: int

    @validator('name')
    def nameValidator(cls, value):
        if(len(value)< 5):
            raise ValueError('name must contain atleast 5 characters')
        return value

try:
    emp1 = Employee(id = 1, age = 34, name='kri')
    print(emp1)
except ValidationError as e:
    print(e.json())

 

Output

[
  {
    "loc": [
      "name"
    ],
    "msg": "name must contain atleast 5 characters",
    "type": "value_error"
  }

Points to remember

a.   Validators in Pydantic are modelled as class methods, so the first argument value they receive is the Employee class, not an instance of Employee.

b.   Second argument is the actual field value to validate.

c.    Validation is done in the order fields are defined.

 

Can I add multiple validators to a pydantic model?

Yes, you can.

 

validator_demo_2.py

from pydantic import BaseModel, ValidationError, validator
from typing import Optional

class Employee(BaseModel):
    id: int
    name: str
    age: int

    @validator('name')
    def nameValidator(cls, value):
        if(len(value)< 5):
            raise ValueError('name must contain atleast 5 characters')
        return value
    
    @validator('age')
    def ageValidator(cls, value):
        if(value < 18):
            raise ValueError('age should be >= 18')
        return value

try:
    emp1 = Employee(id = 1, age = 17, name='kri')
    print(emp1)
except ValidationError as e:
    print(e.json())


Output

[
  {
    "loc": [
      "name"
    ],
    "msg": "name must contain atleast 5 characters",
    "type": "value_error"
  },
  {
    "loc": [
      "age"
    ],
    "msg": "age should be >= 18",
    "type": "value_error"
  }
]


Can I apply a validator on multiple fields

Yes, you can.

 

validator_demo_3.py

from pydantic import BaseModel, ValidationError, validator
from typing import Optional

class Person(BaseModel):
    id: int
    name: str
    age: int

    @validator('age', 'id')
    def ageAndIdValidator(cls, value):
        if(value < 1):
            raise ValueError('id and age must > 0')
        return value

try:
    p1 = Person(id = -1, age = -1, name='kri')
    print(p1)
except ValidationError as e:
    print(e.json())


Output

[
  {
    "loc": [
      "id"
    ],
    "msg": "id and age must > 0",
    "type": "value_error"
  },
  {
    "loc": [
      "age"
    ],
    "msg": "id and age must > 0",
    "type": "value_error"
  }
]


In the above example, I applied validator on both age and id.

 

Can I apply a validator on all the fields of a model?

Yes, by passing the special value '*', you can apply a validator on all the fields of a model.

 

validator_demo_4.py

from pydantic import BaseModel, ValidationError, validator
from typing import Optional

class Box(BaseModel):
    id: int
    weight: str
    height: int

    @validator('*')
    def ageAndIdValidator(cls, value):
        if(value < 1):
            raise ValueError('id, weight and height must > 0')
        return value

try:
    b1 = Box(id = 1, weight = 0, height = 0)
    print(b1)
except ValidationError as e:
    print(e.json())


Output

[
  {
    "loc": [
      "weight"
    ],
    "msg": "'<' not supported between instances of 'str' and 'int'",
    "type": "type_error"
  },
  {
    "loc": [
      "height"
    ],
    "msg": "id, weight and height must > 0",
    "type": "value_error"
  }
]


 

Previous                                                    Next                                                    Home

No comments:

Post a Comment