Method 1: Mastering Pydantic Validators for the Real World Applications
Imagine that we are building a web application for a restaurant where the customers can place orders online. To ensure the orders’ accuracy and prevent mishaps, we decide to use the Pydantic Validators to validate the incoming data.
Whenever we start with the example that requires the Pydantic functions, we need to install its packages first to bring the library with all its packages in the Python dependencies. To bring the necessary dependencies into the project, we use the following commands:
from typing import List
from pydantic import BaseModel, conlist, field_validator
Now that we already installed the required packages, let’s do the first step to implement the example which is to define a data model using Pydantic’s BaseModel. Since we are working on the online restaurant order application, in our case, the model has the attribute as per the requirement of the delivery app. Each order has the customer’s name, the items they want to order, and any special notes. We start by creating a Python class that inherits from Pydantic’s BaseModel and specify the fields we need:
In this model, we now define the data types for each of these attributes where the customer_name is a required string, the items is a list of strings (at least one item required), and the special_notes is an optional string.
customer_name: str
items: List[str]
special_notes: str = None
Conventionally, after defining the model, we create the instance of that model for its validation and then access its object attributes. Still, sometimes, we need to apply some conditions on the data model attributes before creating its object instance. This type of check is known as custom validation. For custom validation, suppose we want to make sure that the customer’s name is at least three characters long and that each item in the order is not empty. We can use Pydantic’s validators for this.
def validate_customer_name(cls, value):
if len(value) < 3:
raise ValueError("Customer name must be at least 3 characters long")
return value
These validators ensure that the customer’s name is long enough and that each item has a meaningful name.
After applying the custom validations, using the validators with the data model and validators in place, we can now use them to validate the incoming orders in our web application.
"customer_name": "John",
"items": ["Burger", "Fries"],
"special_notes": "No onions"
}
try:
order = Order(**order_data)
print("Order is valid:", order)
except ValueError as e:
print("Invalid order:", str(e))
When we create an “Order” instance using the provided data, Pydantic automatically runs the validators that we defined. If any of the validators raise a ValueError, the data is invalid according to our rules.
Here’s the code for the example that we mentioned previously with its output:
from typing import List
from pydantic import BaseModel, conlist, field_validator
class Order(BaseModel):
customer_name: str
items: List[str]
special_notes: str = None
@field_validator('customer_name')
def validate_customer_name(cls, value):
if len(value) < 3:
raise ValueError("Customer name must be at least 3 characters long")
return value
@field_validator('items')
def validate_item(cls, value):
if not value:
raise ValueError("Item names cannot be empty")
return value
order_data = {
"customer_name": "John",
"items": ["Burger", "Fries"],
"special_notes": "No onions"
}
try:
order = Order(**order_data)
print("Order is valid:", order)
except ValueError as e:
print("Invalid order:", str(e))
By mastering the Pydantic Validators in this example, we ensured that the orders placed through our restaurant’s online platform are accurate and gained valuable skills in data validation that can be applied in various contexts. Whether we are working on web applications, APIs, or any other software that deals with data, Pydantic Validators can be a powerful tool in our developer toolkit.
Method 2: Advanced Validation Options in Pydantic
As we become more comfortable with Pydantic Validators, we can explore the advanced features like pre and post-processing functions, working with complex data structures, and utilizing Pydantic’s built-in validators.
For instance, we could add a validator that checks if the order total does not exceed a certain amount or create nested models to handle more complex structures like shipping addresses.
For the Total Order Cost Validator:
In the previously mentioned example, we want to ensure that the total cost of the order doesn’t exceed a certain limit. To achieve this, we can use a validator to check the sum of the costs of each item.
customer_name: str
items: List[str]
special_notes: str = None
@field_validator('items')
def validate_order_cost(cls, value, values):
item_prices = {"Burger": 5, "Fries": 2, "Soda": 1} # Sample item prices
total_cost = sum(item_prices[item] for item in value)
if total_cost > 20:
raise ValueError("Order total cannot exceed $20")
return value
This validator calculates the total cost of the order based on predefined item prices and raises an error if it exceeds the limit.
Regarding the previous example, the nested models for addresses, suppose we want to handle different delivery addresses; we can create a nested model for addresses and validate its components.
street: str
city: str
state: str
zip_code: str
class Order(BaseModel):
customer_name: str
items: conlist(str, min_items=1)
special_notes: str = None
delivery_address: Address
By including the delivery_address as an instance of the “Address” model, we ensure that the address data is complete and follows the required structure.
For the advanced pre-processing, Pydantic allows us to perform the advanced pre-processing using the class methods. For instance, we can automatically capitalize the customer’s name and format the special notes.
customer_name: str
items: List[str]
special_notes: str = None
@classmethod
def pre_process(cls, data):
data['customer_name'] = data['customer_name'].capitalize()
if 'special_notes' in data:
data['special_notes'] = data['special_notes'].strip()
return data
This pre_process method modifies the data before validation, ensuring consistency and improved user experience.
Here’s the full code with the snippet of the output for the observation. This code can be copied to any Python compiler to see the result:
from typing import List
from pydantic import BaseModel, conlist, field_validator
# Nested model for delivery address
class Address(BaseModel):
street: str
city: str
state: str
zip_code: str
# Order model with advanced validation and preprocessing
class Order(BaseModel):
customer_name: str
items: list[str]
special_notes: str = None
delivery_address: Address
@field_validator('items')
def validate_order_cost(cls, value, values):
item_prices = {"Burger": 5, "Fries": 2, "Soda": 1}
total_cost = sum(item_prices[item] for item in value)
if total_cost > 20:
raise ValueError("Order total cannot exceed $20")
return value
@classmethod
def pre_process(cls, data):
data['customer_name'] = data['customer_name'].capitalize()
if 'special_notes' in data:
data['special_notes'] = data['special_notes'].strip()
return data
order_data = {
"customer_name": "alien",
"items": ["Burger", "Fries", "Soda"],
"special_notes": " No onions ",
"delivery_address": {
"street": "123 Main St",
"city": "pwd",
"state": "CA",
"zip_code": "12345"
}
}
# Process order
try:
order_data = Order.pre_process(order_data)
order = Order(**order_data)
print("Order is valid:", order)
except ValueError as e:
print("Invalid order:", str(e))
Mastering the Pydantic Validators beyond the basics helps us to confidently tackle the complex validation scenarios. The library can handle complex structures, integrate advanced preprocessing, and implement customized validators, allowing us to create powerful applications that maintain data integrity and user satisfaction. As we experimented with nested models, more detailed validation rules, and advanced data validation options, we improved our expertise in utilizing Pydantic to its fullest potential. These skills enhance our current project and make us work with a valuable skillset applicable to various development domains.
Conclusion
Becoming skilled with Pydantic Validators helps us to make sure our data is correct and trustworthy. Just like in the restaurant example, we can use Pydantic to set the rules for how the data should look and catch mistakes. We can create better programs by learning about models, custom rules, and advanced features. So, whether we are dealing with online orders or any data, Pydantic makes sure that things work right and the users are satisfied.