# import required modules
from enum import Enum
from typing import Optional
from pydantic import BaseModel, PositiveInt, validator, root_validator, constr
# custom class used as choices for state
# pydantic choices using the built-in Enum of python
# which reduces the need for additional packages
class StateTypes(str, Enum):
DELHI = "DLH"
UTTAR_PRADESH = "UP"
BENGALURU = "BLR"
WEST_BENGAL = "WB"
# class to get personal credentials
class PersonalDetails(BaseModel):
id: int
# constr gives us the ability to specify
# the min and max length
name: constr(min_length=2, max_length=15
phone: PositiveInt
# validation at field level
@validator("phone")
# get phone number
def phone_length(cls, v):
# phone number should typically be of length 10
if len(str(v)) != 10:
raise ValueError("Phone number must be of ten digits")
return v
# class to get address
class Address(BaseModel):
id: int
address_line_1: constr(max_length=50)
# assigning some fields to be optional
address_line_2: Optional[constr(max_length=50)] = None
pincode: PositiveInt
city: constr(max_length=30)
# using choices in python is this simple.
# Just create a class with Enums as choices
# and the pass the class as type for the field
state: StateTypes
@validator("pincode")
def pincode_length(cls, v):
if len(str(v)) != 6:
raise ValueError("Pincode must be of six digits")
return v
# using BaseModels as custom datatypes
# in the User class
class User(BaseModel):
personal_details: PersonalDetails
address: Address
@root_validator(skip_on_failure=True)
# skip_on_failure=True means it will skip the
# validation for this class if it's custom
# fields are not validated
def check_id(cls, values):
# custom validation ensuring personal_details.id is
# same as address.id
personal_details: PersonalDetails = values.get("personal_details")
address: Address = values.get("address")
if personal_details.id != address.id:
raise ValueError(
"ID field of both personal_details as well as address should match"
)
return values
# Driver Code
if __name__ == "__main__":
# testing models
validated_data = {
"personal_details": {
"id": 1,
"name": "GeeksforGeeks",
"phone": 9999999999,
},
"address": {
"id": 1,
"address_line_1": "Sector- 136",
"pincode": 201305,
"city": "Noida",
"state": "UP",
},
}
# this would work without any error as
# no validation will fail
user = User(**validated_data)
# would print the standard __str__ value for the model
print(user)
unvalidated_data = {
"personal_details": {
"id": 1,
"name": "GeeksforGeeks",
"phone": 9999999999,
},
"address": {
"id": 2,
"address_line_1": "Sector- 136",
"pincode": 201305,
"city": "Noida",
"state": "UP",
},
}
# this would raise a value error since the IDs
# are different
user = User(**unvalidated_data)
print(user)