thespacebetweenstars.com

Dynamic Data Models in Python Using Pydantic's Discriminator

Written on

Introduction to Pydantic

In Python development, validating data is crucial for maintaining the integrity and reliability of applications. Whether you're developing a web API, creating a data-centric application, or managing user input, ensuring data accuracy is essential. This is where the Pydantic library excels.

Pydantic is a robust Python library that has gained traction for its efficient and elegant approach to data validation and parsing. It allows developers to define data models in a simple, declarative manner, making it easy to validate and sanitize input data. If you've ever struggled with extensive validation code or intricate data structures, Pydantic can significantly simplify your workflow.

Scope of the Article

This article assumes a basic understanding of how to use Pydantic, as we won't be starting from the ground up. Pydantic has excellent documentation available online, along with a wealth of resources to help newcomers get acquainted with the library.

Today, we will delve into one of Pydantic’s powerful features known as the discriminator, which can be incredibly useful in various scenarios.

Understanding Pydantic's Discriminator

A discriminator in Pydantic is a valuable feature that enables differentiation between various subclasses of a parent model based on the value of a specific field. This is especially useful when working with polymorphic data structures or complex hierarchies.

By incorporating a discriminator field into your Pydantic model, you can dictate which subclass to instantiate during data parsing. This flexibility is particularly beneficial when handling diverse data formats like JSON with varying structures.

Pydantic Discriminator Visual Explanation

This feature not only streamlines data parsing but also enhances code organization and maintainability, making Pydantic an even more powerful tool for complex data validation scenarios.

Use Case Example

Let's consider two models of data that are quite similar but differ in a few fields. For our example, we will create models for dogs and cats.

Dog Model:

from pydantic import BaseModel

class Dog(BaseModel):

age: int

color: str

height: int

endless_love: bool # A boolean indicating endless love

Cat Model:

from pydantic import BaseModel

class Cat(BaseModel):

age: int

color: str

height: int

hairy: bool # A boolean indicating if the cat is hairy

lives: int # The number of lives the cat has

Notice that both models share three common fields: age, color, and height. While this might not be an issue with just a couple of models, managing dozens or hundreds of data models in larger projects could lead to unnecessary redundancy.

Solution with Pydantic's Discriminator

Pydantic offers a solution to this issue by allowing you to define a "parent" model encompassing the shared fields. You can then instantiate one of the subclasses with their unique fields based on a discriminator field specified in the parent class. This leads to an object that combines the parent model's fields with its subclass-specific fields.

Here’s how you can define a parent class for animals:

from typing import Union

from pydantic import BaseModel, Field

class Animal(BaseModel):

pet: Union[Cat, Dog] = Field(..., discriminator='pet_type')

age: int

color: str

height: int

Now, the two initial data models can be reshaped as follows:

from typing_extensions import Literal

from pydantic import BaseModel

class Dog(BaseModel):

pet_type: Literal['dog']

endless_love: bool

class Cat(BaseModel):

pet_type: Literal['cat']

hairy: bool

lives: int

Demo Implementation

Let’s implement this setup by creating an animal object for both a dog and a cat and printing their models:

dog_animal = Animal(

pet={

'pet_type': 'dog',

'endless_love': True

},

age=3,

color="black",

height=50

)

cat_animal = Animal(

pet={

'pet_type': 'cat',

'lives': 9,

'hairy': False

},

age=2,

color="white",

height=35

)

print(dog_animal)

print(cat_animal)

Upon execution, the output will show the properties age, color, and height defined through the Animal model for both objects.

The discriminator field on the pet attribute receives the pet_type value, enabling the instantiation of the correct model for each object. This means that each model is accurately parsed and validated through Pydantic.

Error Handling

Let’s explore what happens if we mistakenly omit a property:

try:

dog_animal = Animal(

pet={

'pet_type': 'dog',

},

age=3,

color="black",

height=50

)

except ValidationError as e:

print(e)

try:

cat_animal = Animal(

pet={

'pet_type': 'cat',

'hairy': False

},

age=2,

color="white",

height=35

)

except ValidationError as e:

print(e)

When executed, this snippet will display validation errors indicating that required fields are missing for both subclasses.

Here’s the complete code snippet for anyone interested in trying it out:

from typing_extensions import Literal

from typing import Union

from pydantic import BaseModel, Field, ValidationError

class Dog(BaseModel):

pet_type: Literal['dog']

endless_love: bool

class Cat(BaseModel):

pet_type: Literal['cat']

lives: int

hairy: bool

class Animal(BaseModel):

pet: Union[Cat, Dog] = Field(..., discriminator='pet_type')

age: int

color: str

height: int

dog_animal = Animal(

pet={

'pet_type': 'dog',

'endless_love': True

},

age=3,

color="black",

height=50

)

cat_animal = Animal(

pet={

'pet_type': 'cat',

'lives': 9,

'hairy': False

},

age=2,

color="white",

height=35

)

print(dog_animal)

print(cat_animal)

try:

dog_animal = Animal(

pet={

'pet_type': 'dog',

},

age=3,

color="black",

height=50

)

except ValidationError as e:

print(e)

try:

cat_animal = Animal(

pet={

'pet_type': 'cat',

'hairy': False

},

age=2,

color="white",

height=35

)

except ValidationError as e:

print(e)

Conclusion

In summary, Pydantic serves as an excellent tool for modeling, validating, and managing data structures in Python. The discriminator feature we explored is just one of its many capabilities, showcasing its potential to simplify complex data handling.

I hope you found this article helpful! If you did, please share your thoughts in the comments and feel free to follow for more content like this.

Video Resources

Learn more about Pydantic and its applications:

This video provides insights into why Pydantic is essential for Python developers in 2024.

This tutorial addresses some of Python's significant challenges and how Pydantic can help solve them.

Share the page:

Twitter Facebook Reddit LinkIn

-----------------------

Recent Post:

Inspiration for Writers: 7 Prompts to Overcome Creative Blocks

Discover seven prompts designed to help writers overcome creative blocks and reignite their passion for writing.

Embracing the Journey of Self-Improvement and Worthiness

Discover the value of self-work and the importance of affirmations in your journey to self-love and growth.

Increasing Your FICO Score: 10 Steps to Boost Your Credit Score

Discover ten effective strategies to improve your FICO score and manage your credit wisely.