Mathew K Analytics

Python library centre

Understanding Pydantic: Efficient Data Validation and Management in Python

Pydantic is a Python library for data validation and settings management. It uses Python type annotations to validate data at runtime. Pydantic ensures data…

⬇ Download notebookOpen in Colab ↗

What you'll learn

Data

No separate download needed — the notebook creates or downloads everything it uses.

📓 Full notebook

Download .ipynb

Introduction to Pydantic#

  • Pydantic is a Python library for data validation and settings management.

  • It uses Python type annotations to validate data at runtime.

  • Pydantic ensures data is the correct type, shape, and format.

  • Why use Pydantic?

  • It reduces bugs by catching errors early.

  • It helps you write clean, readable, and predictable code.

  • Real world uses:

  • Web APIs (like FastAPI) for validating input.

  • Data analysis scripts for checking data schemas.

  • Configuration management.

import warnings; warnings.filterwarnings("ignore")
 
# To install Pydantic on Windows, run this command in your terminal:
# pip install pydantic
 
from pydantic import BaseModel

Core Pydantic Concepts#

  • The main object in Pydantic is the BaseModel.
  • You create models as Python classes that inherit from BaseModel.
  • Models use type annotations to specify expected data types.
  • Validation runs automatically when you create a model.
class Person(BaseModel):
    name: str
    age: int

p = Person(name="Alice", age=30)
print(p)
name='Alice' age=30
p_missing = Person(name="Bob", age="25")
print(p_missing)
name='Bob' age=25

Beginner Examples#

  • Defining and using a Pydantic model is simple.
  • Let us see how to access fields and validate data.
print("Person's name:", p.name)
print("Person's age:", p.age)
Person's name: Alice
Person's age: 30
# Dict conversion
person_dict = p.dict()
print(person_dict)
{'name': 'Alice', 'age': 30}
# Create model from dict
data = {'name': 'Charlie', 'age': 28}
person2 = Person(**data)
print(person2)
name='Charlie' age=28

Intermediate Examples#

  • Pydantic supports default values and optional fields.
  • You can use type hints like Optional[int].
from typing import Optional

class User(BaseModel):
    username: str
    email: str
    bio: Optional[str] = None

user1 = User(username="john", email="john@example.com")
print(user1)
username='john' email='john@example.com' bio=None
# Provide an optional field
user2 = User(username="jane", email="jane@example.com", bio="Python developer")
print(user2)
username='jane' email='jane@example.com' bio='Python developer'
# Model validation error example
try:
    invalid_user = User(username="sam", email=42)
except Exception as e:
    print("Validation error:", e)
Validation error: 1 validation error for User
email
  Input should be a valid string [type=string_type, input_value=42, input_type=int]
    For further information visit https://errors.pydantic.dev/2.11/v/string_type

More Intermediate Features#

  • Pydantic supports nested models and lists.
  • You can model data that contains other models.
class Address(BaseModel):
    street: str
    city: str

class UserWithAddress(BaseModel):
    username: str
    address: Address

addr = Address(street="123 Main St", city="Springfield")
user_addr = UserWithAddress(username="emma", address=addr)
print(user_addr)
username='emma' address=Address(street='123 Main St', city='Springfield')
# Nested models as dict
print(user_addr.dict())
{'username': 'emma', 'address': {'street': '123 Main St', 'city': 'Springfield'}}

Advanced Examples#

  • Pydantic models can validate and parse JSON data.
  • You can customize validation logic with special methods.
  • Let us see an example using custom validation.
from pydantic import validator

class Product(BaseModel):
    name: str
    price: float

    @validator('price')
    def price_must_be_positive(cls, v):
        if v < 0:
            raise ValueError('Price must be positive')
        return v

p1 = Product(name="Book", price=12.50)
print(p1)

try:
    p2 = Product(name="Chair", price=-5)
except Exception as e:
    print(e)
name='Book' price=12.5
1 validation error for Product
price
  Value error, Price must be positive [type=value_error, input_value=-5, input_type=int]
    For further information visit https://errors.pydantic.dev/2.11/v/value_error
import json
json_data = '{"name": "Monitor", "price": 120.0}'
parsed = Product.parse_raw(json_data)
print(parsed)
name='Monitor' price=120.0
# Working with lists of objects
class Order(BaseModel):
    products: list[Product]

order = Order(products=[Product(name="Pen", price=1.2), Product(name="Notebook", price=2.5)])
print(order)
products=[Product(name='Pen', price=1.2), Product(name='Notebook', price=2.5)]

Error Handling and Debugging#

  • Pydantic raises clear error messages when data is invalid.
  • Use try-except to catch and handle errors safely.
  • Let us see an example:
try:
    bad_order = Order(products=[{"name": "Lamp", "price": "free"}])
except Exception as error:
    print("Order validation failed:", error)
Order validation failed: 1 validation error for Order
products.0.price
  Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='free', input_type=str]
    For further information visit https://errors.pydantic.dev/2.11/v/float_parsing

Best Practices#

  • Use type annotations for all fields.
  • Use Optional[] for fields that can be empty.
  • Handle exceptions when loading data from external sources.
  • Use validators to enforce extra constraints.
  • Convert models to dict for JSON or database storage.

Mini-Project: Simple Address Book#

  • Let us build a tiny address book using Pydantic.
  • It will store a list of people and their contact details.
  • We will create, add, and display contacts.
class Contact(BaseModel):
    name: str
    phone: str
    email: Optional[str] = None

class AddressBook(BaseModel):
    contacts: list[Contact] = []

abook = AddressBook()
print(abook)
contacts=[]
# Add contacts
contact1 = Contact(name="Anna", phone="555-1234", email="anna@mail.com")
contact2 = Contact(name="Ben", phone="555-2345")
abook.contacts.append(contact1)
abook.contacts.append(contact2)
print(abook)
contacts=[Contact(name='Anna', phone='555-1234', email='anna@mail.com'), Contact(name='Ben', phone='555-2345', email=None)]
# Show all contacts
for c in abook.contacts:
    print(f"Name: {c.name}, Phone: {c.phone}, Email: {c.email}")
Name: Anna, Phone: 555-1234, Email: anna@mail.com
Name: Ben, Phone: 555-2345, Email: None

Thank You!#

  • If you enjoyed this lesson, please like and subscribe.
  • Find more tutorials on our channel!

Found this useful?

All lessons, notebooks and datasets here are free. If they helped you, a coffee keeps new lessons coming.