FastAPI

Dataclasses vs Pydantic — Python Data Validation Comparison

Thirdy Gayares
12 min read

🎓 What You Will Learn

  • Dataclasses: Built-in Python data structures
  • Pydantic: Advanced validation and serialization
  • Use Cases: When to use each approach
  • Performance: Speed and memory tradeoffs
  • FastAPI Integration: How each works with FastAPI
  • Best Practices: Industry recommendations
PythonDataclassesPydanticFastAPI

1Overview: What Are They?

Both dataclasses and Pydantic models store structured data. Dataclasses are lightweight and built into Python. Pydantic is a powerful validation library.

Simple Rule: Use dataclasses for simple data, Pydantic for validation-heavy code.

2Dataclasses: The Basics

Python's built-in dataclasses (Python 3.7+) provide a clean syntax for defining classes with auto-generated __init__, __repr__, and more.

example.py
from dataclasses import dataclass

@dataclass
class User:
    name: str
    email: str
    age: int

# Usage
user = User(name="John", email="[email protected]", age=30)
print(user)  # User(name='John', email='[email protected]', age=30)

3Pydantic: The Powerhouse

Pydantic models validate data automatically and provide JSON serialization out of the box.

example.py
# EmailStr needs: pip install "pydantic[email]"
from pydantic import BaseModel, EmailStr

class User(BaseModel):
    name: str
    email: EmailStr  # Validates email format!
    age: int

# Usage with validation
user = User(name="John", email="[email protected]", age=30)
print(user.model_dump_json())  # Auto-serializes to JSON!

# Invalid email throws error automatically
invalid = User(name="John", email="not-an-email", age=30)  # ❌ ValidationError

🔍 Interactive Comparison

Dataclasses

❌ No built-in validation

Pydantic

✅ Automatic validation on creation

💡 Click each tab to see detailed comparisons!

4Validation Capabilities

Dataclasses require manual validation. Pydantic validates automatically.

dataclass_validation.py
from dataclasses import dataclass

@dataclass
class User:
    name: str
    age: int

    def __post_init__(self):
        # Manual validation
        if not isinstance(self.name, str):
            raise ValueError("Name must be string")
        if self.age < 0:
            raise ValueError("Age cannot be negative")
pydantic_validation.py
from pydantic import BaseModel, Field

class User(BaseModel):
    name: str
    age: int = Field(gt=0)  # Automatic validation!

# This works
user = User(name="John", age=30)

# This fails automatically
invalid = User(name="John", age=-5)  # ❌ ValidationError

5Serialization & Deserialization

Pydantic excels at converting to/from JSON. Dataclasses need custom code.

dataclass_json.py
from dataclasses import dataclass, asdict
import json

@dataclass
class User:
    name: str
    email: str

user = User(name="John", email="[email protected]")

# Manual JSON conversion
json_str = json.dumps(asdict(user))
pydantic_json.py
from pydantic import BaseModel

class User(BaseModel):
    name: str
    email: str

user = User(name="John", email="[email protected]")

# Automatic JSON conversion
json_str = user.model_dump_json()
print(json_str)  # {"name":"John","email":"[email protected]"}

6FastAPI Integration

FastAPI was built for Pydantic models. They work seamlessly together.

main.py
from fastapi import FastAPI
from pydantic import BaseModel

app = FastAPI()

class User(BaseModel):
    name: str
    email: str

@app.post("/users")
def create_user(user: User):  # Automatic validation + JSON parsing!
    return user

FastAPI Advantage: Pydantic models automatically validate request bodies, serialize responses, and generate OpenAPI docs.

7Performance Comparison

Dataclasses are faster because they skip validation. Pydantic does extra work, so it is a bit slower. Pydantic v2 has a Rust core, so the cost is small for most apps.

OperationDataclassPydantic
Object CreationFastest (no checks)Fast (runs validation)
ValidationManual (you write it)Built-in
JSON SerializationManual (json + asdict)Built-in (model_dump_json)
Runtime Type CheckingNoYes

8When to Use Each

Choose based on your needs:

Use Dataclasses when:

  • Building simple data structures
  • Performance is critical
  • No validation needed
  • No JSON serialization required

Use Pydantic when:

  • Building FastAPI endpoints
  • Need automatic validation
  • Converting to/from JSON frequently
  • Complex type checking needed

9Advanced Pydantic Features

Pydantic provides powerful validation features dataclasses can't match easily.

advanced_pydantic.py
from pydantic import BaseModel, Field, field_validator

class User(BaseModel):
    name: str = Field(min_length=1, max_length=100)
    email: str
    age: int = Field(gt=0, lt=150)

    @field_validator("email")
    @classmethod
    def validate_email(cls, v: str) -> str:
        if "@" not in v:
            raise ValueError("Invalid email")
        return v

    @field_validator("name")
    @classmethod
    def name_must_contain_space(cls, v: str) -> str:
        if " " not in v:
            raise ValueError("Full name required")
        return v

Pydantic v2 tip: Use @field_validator. The old @validator is from Pydantic v1 and is deprecated.

10Custom Validators in Pydantic

Create reusable validation logic easily.

validators.py
from pydantic import BaseModel, field_validator

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

    @field_validator("price")
    @classmethod
    def price_must_be_positive(cls, v):
        if v <= 0:
            raise ValueError("Price must be positive")
        return v

11Inheritance & Composition

Both support inheritance. In Pydantic, child models also keep all validation from the parent.

inheritance.py
from pydantic import BaseModel

class BaseUser(BaseModel):
    name: str
    email: str

class AdminUser(BaseUser):
    is_admin: bool = True
    permissions: list[str] = []

# AdminUser inherits all validation from BaseUser
admin = AdminUser(name="Admin", email="[email protected]")

12Decision Tree

Quick reference for choosing between them:

Is this for FastAPI?
→ YES: Use Pydantic ✅
→ NO: Do you need validation?
    → YES: Use Pydantic ✅
    → NO: Use Dataclasses ⚡

Good to know: FastAPI does accept dataclasses. It converts them to Pydantic dataclasses behind the scenes. But you lose extras like EmailStr and Field rules. For request bodies, stick with BaseModel.

13Migrating from Dataclasses to Pydantic

It's easy to convert dataclasses to Pydantic models.

migration.py
# Before: Dataclass
from dataclasses import dataclass

@dataclass
class User:
    name: str
    email: str

# After: Pydantic
from pydantic import BaseModel

class User(BaseModel):
    name: str
    email: str

14Testing Both Approaches

Both are easy to test.

tests.py
# EmailStr needs: pip install "pydantic[email]"
from pydantic import BaseModel, EmailStr, ValidationError
import pytest

class User(BaseModel):
    name: str
    email: EmailStr

def test_valid_user():
    user = User(name="John", email="[email protected]")
    assert user.name == "John"

def test_invalid_email():
    # Plain str would accept "invalid" — EmailStr rejects it
    with pytest.raises(ValidationError):
        User(name="John", email="invalid")

15Resources & Summary

You now know when to use dataclasses vs Pydantic. The rule is simple: use Pydantic in FastAPI. Use dataclasses for simple internal data that needs no validation.

You can now choose the right tool for data modeling in Python. Use Pydantic for FastAPI and validation-heavy code!

About the Author

TG

Thirdy Gayares

Passionate developer creating custom solutions for everyone. I specialize in building user-friendly tools that solve real-world problems while maintaining the highest standards of security and privacy.