Python

How to Get Started with Pydantic: A Beginner’s Tutorial

Before using, importing, or processing the data, it is necessary to validate its correctness and quality. Various validation approaches and techniques might be used depending on the constraints or purposes. Data validation is made simpler with the help of the Pydantic. It is a powerful tool that can be used for various purposes including creating APIs, managing databases, data processing, and handling. The library offers a simple and clear syntax that allows for easy development and validation of data models. This includes the ability to define the field types, default values, and limitation rules directly within the code which makes it easier to understand and maintain.

Pydantic also enables us to develop the custom validators for additional data definition and validation. In this post, we’ll look at the basics of Pydantic and how you can use it to validate the data.

Why Use Pydantic?

  1. All levels of developers can use Pydantic because it is user-friendly and has a simple API.
  2. It can be used in high-performance applications since it performs quick and effective data validation.
  3. Pydantic can automatically generate the documentation or comments for your data models which saves you time and makes it simpler to comprehend the structure of your data.
  4. It supports Type hints, making it simpler to specify the data structure and prevent errors.
  5. It easily integrates with FastAPI to enable the API request and response validation.
  6. Pydantic enables the developers to build custom validation rules, allowing them to implement the additional validation logic if required.
  7. It ensures that the defined and input data is consistent and fulfills the required standards. As a result, the errors are reduced and the codebase is easier to maintain.

How Pydantic Works

Pydantic enables us to create the data models using classes. These classes can contain default values, type hints, and validation rules, and inherit the “BaseModel” class. This is a class that defines the structure of the data and the validation requirements must meet in order to be valid.  Pydantic checks the incoming data to ensure that it complies with the requirements using the data model after receiving it.

Pydantic assesses each field in the data during validation to the validation rules and type hints that are specified in the data model. Pydantic generates an error and ends the validation process if the data does not meet the requirements. If the data is accurate, Pydantic generates an instance of the model, fills it with the entered data, and then gives the output to the user.

Example of Using Pydantic

Let’s demonstrate the use of Pydantic with the help of an example. We begin by importing the “BaseModel” class from which our data models should be inherited. We also import the ValidationError exception which we use to handle the errors if our model’s data parsing and validation fail.

Code:

from pydantic import BaseModel, ValidationError

Next, we define a model class. To demonstrate, we create an example “Garments” class with the following fields:

type: The type of garment. Type the hint that is specified as a string.

color: The color of the garment. Type the hint that is specified as a string.

size: An integer that represents the size of the garment. Type the hint that is specified as int.

is_available: A Boolean value that indicates if the garment is available or not.

Our “Garments” class should inherit from the imported “BaseModel” class.

Code:

class Garments(BaseModel):
    type: str
    color: str
    size: int
    is_available: bool

After defining the model for our “Garments” class, we create a dictionary that contains the data that is parsed and validated against our model. We create a dictionary with the valid data first.

garments_data = {
    'type': 'shirt',
    'color': 'red',
    'size': 42,
    'is_available': True
}

After that, an object of the “Garments” class is created. We provide the fields from our data dictionary as input to the constructor. To make our script compact, we employ the dictionary unpacking operator (denoted by a double asterisk).

garments = Garments(**garments_data)

While we provide a dictionary of data that aligns with our model’s requirements, it’s important to remember that this might not always hold true for external data inputs. Therefore, in such cases where the data is not valid, it triggers a ValidationError.

Now, we enclose the instantiation of our “Garments” class object inside a try-except block.

try:
    garments = Garments(**garments_data)
    print("Data:")
    #add data here
except ValidationError as e:
    print("Validation Error:")
    print(e)

To display the output in the form of a Python dictionary, let’s use the “dict” method to convert our model object into a dictionary. Although this function may accept extra inputs to customize the model-to-dictionary conversion, we will not pass any parameters to get the default output.

print(garments.dict())

Output:

Now, let’s make some changes to make our data invalid.

We specify the type attribute with an integer value and the size attribute with a string value.

# Invalid garments data
garments_data = {
    'type': 40,
    'color': 'red',
    'size': “forty two”,
    'is_available': True
}

Now, if we execute our code again, it displays an exception.

As can be seen, it indicates that the type field input should be a string, but it is currently specified as an int value, and the size field is expected to contain a valid int but the provided input value is of a string type.

Error Handling

When using an invalid data object in the previous section, we simply handle the error that occurs by printing the raised exception. There are also various exception methods that we can use to get the validation errors. There are also other methods to obtain the information from a ValidationError object such as e.errors(), e.json(), and str(e), etc. Exceptions with the following properties are returned by the errors() method:

loc: The location of the error.

type: The error type.

msg: A message in a human-readable form describing the error.

Handling errors has become more flexible and straightforward using these methods. One such example is transforming the error messages into different languages or mapping this format to particular error messages.

Now, let’s provide our model with some invalid data and use these methods to handle the raised error.

from pydantic import BaseModel, ValidationError
class Garments(BaseModel):
    type: str
    color: str
    size: int
    is_available: bool

garments_data = {
    'type': 40,
    'color': 'red',
    'size': "forty two",
    'is_available': True
}
try:
    garments = Garments(**garments_data)
    print("Invalid Data:")
    print(garments.dict())

except ValidationError as e:
    v_errors = e.errors()
    print(v_errors,"\n")
    print(v_errors[0],"\n")
    print(v_errors[1]["loc"],"\n")
    print(v_errors[1]["msg"],"\n")
    print(v_errors[1]["type"])

Output:

As you can see, “v_errors[0]” displays where the first error occurred. Whereas “v_errors[1][“loc”]” displays the location of the second exception/error that appears in our program and so on.

Conclusion

As we’ve seen in this article, Pydantic is easy to use and has several built-in classes and decorators that help in effective validation, data modeling, and error handling. Using an example, we demonstrated how to use Pydantic to validate your data. First, we passed our data model with valid data and then used the invalid data to show how Pydantic handles the errors if an exception occurs.

About the author

Omar Farooq

Hello Readers, I am Omar and I have been writing technical articles from last decade. You can check out my writing pieces.