Python

How to Streamline Your Data Models with Pydantic Inheritance

Simplifying the data models with Pydantic Inheritance involves creating a basic model with shared traits and rules. More specific models then use this basic model, adding unique details. This reduces repeating code and sets a clear model order, making the code reusable and the consistency easier. This method simplifies checking the data, turning it into a useful format, and changing it. Developers can easily add to models as needed while still benefiting from Pydantic’s built-in checks and formatting, resulting in efficient and easy-to-manage code for working with data.

Example 1: Streamlining the Data Models for an E-Commerce Platform

To simplify our data models, using Pydantic Inheritance can significantly enhance the efficiency and maintainability of your codebase. This technique creates a fundamental base model with shared attributes and validation logic. After that, more specialized models can inherit from this base which allows us to add specific fields and customizations while avoiding adding codes that are not required.

For instance, think of developing an e-commerce platform. We can define a basic “Product” model in Pydantic that includes common attributes like name, description, and price. As our platform grows, we might introduce various product types such as Electrical appliances which are electronic products, and clothing products. By inheriting from the “Product” base model, we can effortlessly include additional attributes for each type like battery_life for electronics and size for clothing.

Let’s practically do a concrete example of this concept. Consider a scenario where we manage a library of physical and e-books (electronic books). First, create a base model called “BookBase” with the required fields that are common to all types of books such as title, author, and publication_year. We implement the validation rules within this base model to ensure the data integrity.

!pip install pydantic
from pydantic import BaseModel, field_validator

class BookBase(BaseModel):
    title: str
    author: str
    publication_year: int

In defining the base model, we create a base model that serves as the foundation for our specialized models. This base model contains attributes and validation logic that are common to all the models that inherit from it. In the provided example, the base model is called “BookBase”.

“BookBase” is defined as a subclass of “BaseModel” from the Pydantic library. It includes three attributes: book title, author, and publication_year.

The “@filed_validator” decorator is used to apply a validation function to the publication_year attribute. This ensures that the publication year remains within a valid range.

@field_validator("publication_year")

def validate_publication_year(cls, year):

  if year < 1000 or year > 2100:

    raise ValueError("Invalid publication year")

  return year

Now comes the creation of specialized models. In this step, we define the specialized models that inherit from the base model. These specialized models can have additional attributes that are unique to their data type. In this example, two specialized models are created: “PhysicalBook” and “ElectronicBook”.

class PhysicalBook(BookBase):
    pages: int
    weight_grams: int

class ElectronicBook(BookBase):
    file_format: str
    file_size_mb: float

“PhysicalBook” and “ElectronicBook” are defined as subclasses of “BookBase”, inheriting all attributes and validation rules from it. “PhysicalBook” includes two additional attributes pages and the weight of the book. “ElectronicBook” includes two additional attributes: file_format and file_size_mb.

physical_book_data = {
    "title": "The Catcher in the Rye",
    "author": "J.D. Salinger",
    "publication_year": 1951,
    "pages": 224,
    "weight_grams": 350
}

electronic_book_data = {
    "title": "1984",
    "author": "George Orwell",
    "publication_year": 1949,
    "file_format": "PDF",
    "file_size_mb": 2.5
}

physical_book = PhysicalBook(**physical_book_data)
electronic_book = ElectronicBook(**electronic_book_data)

Then, we instantiate the specialized models. Instantiate means that we create the instances of the specialized models using the data that stick to the defined attributes and validation rules.

The physical_book_data and electronic_book_data are dictionaries that contain the attribute and value pairs for each specialized model. The “PhysicalBook” instance is created using the “PhysicalBook” class and the provided data. The “ElectronicBook” instance is created using the “ElectronicBook” class and the provided data.

Full code:

!pip install pydantic
from pydantic import BaseModel, field_validator

class BookBase(BaseModel):
    title: str
    author: str
    publication_year: int

    @field_validator("publication_year")
    def validate_publication_year(cls, year):
        if year < 1000 or year > 2100:
            raise ValueError("Invalid publication year")
        return year
class PhysicalBook(BookBase):
    pages: int
    weight_grams: int

class ElectronicBook(BookBase):
    file_format: str
    file_size_mb: float
physical_book_data = {
    "title": "The Catcher in the Rye",
    "author": "J.D. Salinger",
    "publication_year": 1951,
    "pages": 224,
    "weight_grams": 350
}

electronic_book_data = {
    "title": "1984",
    "author": "George Orwell",
    "publication_year": 1949,
    "file_format": "PDF",
    "file_size_mb": 2.5
}

physical_book = PhysicalBook(**physical_book_data)
electronic_book = ElectronicBook(**electronic_book_data)
print (physical_book)
print (electronic_book)

We create a precise model hierarchy that shares the common attributes and validation rules by following these steps. This approach reduces code duplication, ensures consistent data handling, and allows for easy extension and customization as our application grows.

Adopting Pydantic Inheritance makes our data models more organized, strong, and scalable. This approach enhances code reusability, minimizes the load of extra models/code, and ensures a consistent data handling throughout our application.

Example 2: Streamlining the Data Models for a Fitness App

Let’s suppose we are developing a fitness tracking app that needs to handle different types of exercises, including cardio and strength training. Using Pydantic Inheritance, we can create a well-structured system to manage these exercise types efficiently.

First and foremost, we define the base model. Start by defining a base model named “ExerciseBase” that includes the attributes that are common to all exercise types:

!pip install pydantic

from pydantic import BaseModel, PositiveInt, field_validator

class ExerciseBase(BaseModel):

  name: str

  duration_minutes: PositiveInt

In this example, the base model includes attributes like name (exercise name) and duration_minutes (exercise duration in minutes). The “@field_validator” decorator makes sure that the duration is a positive integer.

@field_validator("duration_minutes")

def validate_duration_minutes(cls, value):

  if value <= 0:

    raise ValueError("Duration must be a positive integer")

  return value

Next, create specialized models for different exercise types by inheriting from the “ExerciseBase”.

Here, the “CardioExercise” and “StrengthExercise” inherit the attributes from “ExerciseBase” and add the attributes that are specific to each exercise type. For instance, “CardioExercise” includes an intensity attribute, and “StrengthExercise” includes attributes for sets and repetitions.

class CardioExercise(ExerciseBase):

intensity: str  # e.g, low, moderate, high

To utilize the specialized models that we created, we can create instances of the specialized models and use them within our fitness app:

class StrengthExercise(ExerciseBase):
    sets: PositiveInt
    repetitions: PositiveInt
cardio_data = {
    "name": "Running",
    "duration_minutes": 30,
    "intensity": "moderate"
}

strength_data = {
    "name": "Push-ups",
    "duration_minutes": 15,
    "sets": 3,
    "repetitions": 15
}
cardio_workout = CardioExercise(**cardio_data)
strength_workout = StrengthExercise(**strength_data)

Cardio_workout and strength_workout are the specialized models that are created using appropriate data in this example. The data sticks to the attributes and validation rules that are defined in the base and technological models.

!pip install pydantic
from pydantic import BaseModel, PositiveInt, field_validator

class ExerciseBase(BaseModel):
    name: str
    duration_minutes: PositiveInt

    @field_validator("duration_minutes")
    def validate_duration_minutes(cls, value):
        if value <= 0:
            raise ValueError("Duration must be a positive integer")
        return value
class CardioExercise(ExerciseBase):
    intensity: str  # e.g, low, moderate, high

class StrengthExercise(ExerciseBase):
    sets: PositiveInt
    repetitions: PositiveInt
cardio_data = {
    "name": "Running",
    "duration_minutes": 30,
    "intensity": "moderate"
}

strength_data = {
    "name": "Push-ups",
    "duration_minutes": 15,
    "sets": 3,
    "repetitions": 15
}

cardio_workout = CardioExercise(**cardio_data)
strength_workout = StrengthExercise(**strength_data)
print (cardio_workout)
print (strength_workout)

There are many benefits of Pydantic Inheritance to streamline the data models. They help in code organization using inheritance. We create a clear hierarchy of models, making our codebase organized and easy to use. It also offers code reusability, common attributes, and validation logic involved in the base model, reducing error and ensuring consistent behavior across different exercise types. For customization, specialized models allow us to add the exercise-specific attributes and validation rules while maintaining a consistent structure. For data integrity, the built-in validation ensures that the data remains to the predefined rules, enhancing data integrity and reducing errors.

Following these steps can effectively streamline our data models, making our fitness app’s codebase cleaner, more maintainable, and adaptable to future changes.

Conclusion

Utilizing the Pydantic Inheritance to combine the data models offers a solid approach to creating organized, adaptable, and efficient codebases. The development process becomes more systematic by establishing a foundational base model and later specialized models. This strategy reduces extra models, ensures data consistency, and facilitates future modifications. Whether designing an e-commerce platform with varying product types or a fitness app with diverse exercise categories, Pydantic Inheritance simplifies the data management and code reusability to validation standards.

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.