Reference:
https://betterprogramming.pub/the-beginners-guide-to-pydantic-ba33b26cde89
pydantic enforces type hints at runtime, and provides user friendly errors when data is invalid.
Defining an object in pydantic
is as simple as creating a new class which inherits from theBaseModel
. When you create a new object from the class, pydantic
guarantees that the fields of the resultant model instance will conform to the field types defined on the model.
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel
Declare a new class which inherits the BaseModel
as follow:
class User(BaseModel):
id: int
username : str
password : str
confirm_password : str
alias = 'anonymous'
timestamp: Optional[datetime] = None
friends: List[int] = []
pydantic
uses the built-in type hinting syntax to determine the data type of each variable.
The next step is to instantiate a new object from the User
class.
data = {'id': '1234', 'username': 'wai foong', 'password': 'Password123', 'confirm_password': 'Password123', 'timestamp': '2020-08-03 10:30', 'friends': [1, '2', b'3']}user = User(**data)
You should get the following output when you print out the user
variable. You can notice that id
has been automatically converted to an integer, even though the input is a string. Likewise, bytes are automatically converted to integers, as shown by the friends
field.
id=1234 username='wai foong' password='Password123' confirm_password='Password123' timestamp=datetime.datetime(2020, 8, 3, 10, 30) friends=[1, 2, 3] alias='anonymous'
Methods and attributes under BaseModel
Classes that inherit the BaseModel
will have the following methods and attributes:
dict()
— returns a dictionary of the model’s fields and valuesjson()
— returns a JSON string representation dictionarycopy()
— returns a deep copy of the modelparse_obj()
— a utility for loading any object into a model with error handling if the object is not a dictionaryparse_raw()
— a utility for loading strings of numerous formatsparse_field()
— similar to parse_raw()
but meant for filesfrom_orm()
— loads data into a model from an arbitrary classschema()
— returns a dictionary representing the model as JSON schemaschema_json()
— returns a JSON string representation of schema()
construct()
— a class method for creating models without running validation__fields_set__
— Set of names of fields which were set when the model instance was initialized__fields__
— a dictionary of the model’s fields__config__
— the configuration class for the model
ValidationError
In order to get better details on the error, it is highly recommended to wrap it inside a try-catch block, as follows:
from pydantic import BaseModel, ValidationError# ... codes for User classdata = {'id': 'a random string', 'username': 'wai foong', 'password': 'Password123', 'confirm_password': 'Password123', 'timestamp': '2020-08-03 10:30', 'friends': [1, '2', b'3']}try:
user = User(**data)
except ValidationError as e:
print(e.json())
It will print out the following JSON, which indicates that the input for id
is not a valid integer.
Root Validator
Validation can also be performed on the entire model's data.
As with field validators, root validators can have pre=True
, in which case they're called before field validation occurs (and are provided with the raw input data), or pre=False
(the default), in which case they're called after field validation.
Field validation will not occur if pre=True
root validators raise an error. As with field validators, "post" (i.e. pre=False
) root validators by default will be called even if prior validators fail; this behaviour can be changed by setting the skip_on_failure=True
keyword argument to the validator. The values
argument will be a dict containing the values which passed field validation and field defaults where applicable.
Validator
Furthermore, you can create your own custom validators using the validator
decorator inside your inherited class. Let’s have a look at the following example which determine if the id
is of four digits and whether the confirm_password
matches the password
field.
from datetime import datetime
from typing import List, Optional
from pydantic import BaseModel, ValidationError, validatorclass User(BaseModel):
id: int
username : str
password : str
confirm_password : str
alias = 'anonymous'
timestamp: Optional[datetime] = None
friends: List[int] = [] @validator('id')
def id_must_be_4_digits(cls, v):
if len(str(v)) != 4:
raise ValueError('must be 4 digits')
return v @validator('confirm_password')
def passwords_match(cls, v, values, **kwargs):
if 'password' in values and v != values['password']:
raise ValueError('passwords do not match')
return v