DEV Community

Muhammad Atif Iqbal
Muhammad Atif Iqbal

Posted on

Understanding Pydantic Model Validation with Simple Examples

Pydantic is a Python library that provides runtime type checking and validation of data. Let me explain how validation works with simple examples.

Basic Example Without ORM

First, let's look at a simple Pydantic model without database/ORM involvement:

from pydantic import BaseModel

class Person(BaseModel):
    name: str
    age: int

# Valid input (dictionary)
valid_data = {"name": "Alice", "age": 30}
person = Person(**valid_data)  # or Person.model_validate(valid_data)
print(person)  # Works fine

# Invalid input
invalid_data = {"name": "Bob", "age": "thirty"}  # age should be int
try:
    person = Person(**invalid_data)
except Exception as e:
    print(f"Error: {e}")  # Shows validation error for age
Enter fullscreen mode Exit fullscreen mode

The Problem With ORM Objects

When working with SQLAlchemy ORM models, we can't directly use them with Pydantic because:

# SQLAlchemy model
class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String)

# Pydantic model
class UserResponse(BaseModel):
    id: int
    name: str

# This WON'T work directly:
db_user = User(id=1, name="Alice")
try:
    response = UserResponse.model_validate(db_user)
except Exception as e:
    print(f"Error: {e}")  # Fails because Pydantic doesn't know how to handle ORM objects
Enter fullscreen mode Exit fullscreen mode

Solution: from_attributes=True

This is where from_attributes=True comes in. It tells Pydantic:

  1. "When I give you an ORM object, don't treat it as a dictionary"
  2. "Instead, access its attributes directly (like db_user.id, db_user.name)"
class UserResponse(BaseModel):
    id: int
    name: str

    class Config:
        from_attributes = True  # Previously called orm_mode=True

# Now this WORKS:
db_user = User(id=1, name="Alice")
response = UserResponse.model_validate(db_user)
print(response)  # UserResponse(id=1, name='Alice')
Enter fullscreen mode Exit fullscreen mode

How It Works Step-by-Step

  1. You pass an ORM object to model_validate()
  2. Pydantic checks from_attributes in the Config
  3. Instead of treating it as a dict, Pydantic:
    • Looks at the field names in your Pydantic model (id, name)
    • Tries to access those attributes on the ORM object (db_user.id, db_user.name)
    • Validates the values it finds

Real-world Example

Your WorkHistoryResponse should be:

class WorkHistoryResponse(BaseModel):
    id: int
    user_id: int
    facility_name: str
    # ... other fields ...

    class Config:
        from_attributes = True  # This enables ORM model conversion
Enter fullscreen mode Exit fullscreen mode

Then when you do:

WorkHistoryResponse.model_validate(work_history_orm_object)
Enter fullscreen mode Exit fullscreen mode

Pydantic will:

  1. See from_attributes=True
  2. Look for work_history_orm_object.id
  3. Look for work_history_orm_object.user_id
  4. Look for work_history_orm_object.facility_name
  5. And so on for all fields in response model

Key Takeaways

  1. Direct dictionaries work automatically with Pydantic
  2. ORM objects need from_attributes=True to be validated
  3. The Config class must be inside your Pydantic model
  4. Field names must match between your ORM model and Pydantic model

Top comments (0)