We use cookies to enhance your experience on the site
CodeWorlds

Pydantic - data validation powered by Rust

Welcome, @name! Darwin here with Pydantic - the most powerful data validation system in Python! 📊✅

Pydantic is the core of FastAPI - every request/response goes through Pydantic validation. In version 2.0 it was rewritten in Rust and is 17x faster! ⚡🦀

Safari Analogy: Pydantic is like a quality control system for observations - it checks whether each observation has correct data (species exists, population is a number, date is valid). If something is wrong, it reports an error immediately! 🔍✅

Pydantic Basics

BaseModel - the base class

1from pydantic import BaseModel
2
3class Species(BaseModel):
4    id: int
5    name: str
6    population: int
7    endangered: bool = False  # Default value
8
9# Creating an instance
10lion = Species(id=1, name="Lion", population=120, endangered=True)
11
12print(lion.name)         # "Lion"
13print(lion.dict())       # {'id': 1, 'name': 'Lion', 'population': 120, 'endangered': True}
14print(lion.json())       # JSON string

Automatic type conversion

1# Pydantic automatically converts types!
2species = Species(id="1", name="Lion", population="120", endangered="yes")
3
4print(type(species.id))         # <class 'int'>  (conversion str→int)
5print(type(species.population)) # <class 'int'>
6print(species.endangered)       # True  (conversion "yes"→True)

Validation errors

1try:
2    # Invalid data
3    species = Species(id="abc", name="Lion", population=-50)
4except ValidationError as e:
5    print(e.json())  # Detailed validation errors

Field - advanced validation

1from pydantic import BaseModel, Field
2
3class Species(BaseModel):
4    id: int = Field(..., gt=0, description="Unique species ID")
5    name: str = Field(..., min_length=2, max_length=100)
6    scientific_name: str = Field(..., pattern=r'^[A-Z][a-z]+ [a-z]+$')  # Regex
7    population: int = Field(..., ge=0, le=1_000_000)  # >= 0, <= 1M
8    habitat: str = Field(default="unknown")
9
10# Validation works!
11lion = Species(
12    id=1,
13    name="Lion",
14    scientific_name="Panthera leo",  # Must match the regex
15    population=120
16)

Field validators:

  • gt
    ,
    ge
    - greater than, greater or equal
  • lt
    ,
    le
    - less than, less or equal
  • min_length
    ,
    max_length
    - string length
  • pattern
    - regex validation
  • description
    - description for documentation

Custom validators

1from pydantic import BaseModel, field_validator
2
3class Species(BaseModel):
4    name: str
5    population: int
6
7    @field_validator('name')
8    @classmethod
9    def name_must_be_capitalized(cls, v):
10        if not v[0].isupper():
11            raise ValueError('Name must start with an uppercase letter')
12        return v
13
14    @field_validator('population')
15    @classmethod
16    def population_realistic(cls, v):
17        if v > 1_000_000:
18            raise ValueError('Population exceeds realistic range')
19        return v
20
21# Validation works
22lion = Species(name="Lion", population=120)  # OK
23lion = Species(name="lion", population=120)  # ValueError!

Safari API with Pydantic

1from fastapi import FastAPI, HTTPException
2from pydantic import BaseModel, Field, field_validator
3from typing import Literal
4from datetime import datetime
5
6app = FastAPI()
7
8class SpeciesBase(BaseModel):
9    name: str = Field(..., min_length=2, max_length=100)
10    scientific_name: str = Field(..., pattern=r'^[A-Z][a-z]+ [a-z]+$')
11    population: int = Field(..., ge=0, le=1_000_000)
12    habitat: Literal["savanna", "forest", "desert", "wetland"]
13
14    @field_validator('scientific_name')
15    @classmethod
16    def validate_scientific_name(cls, v):
17        parts = v.split()
18        if len(parts) != 2:
19            raise ValueError('Scientific name must follow the format: Genus species')
20        return v
21
22class SpeciesCreate(SpeciesBase):
23    pass
24
25class Species(SpeciesBase):
26    id: int
27    created_at: datetime = Field(default_factory=datetime.now)
28
29    class Config:
30        from_attributes = True
31
32@app.post("/species", response_model=Species)
33async def create_species(species: SpeciesCreate):
34    # Pydantic automatically validates the data!
35    new_species = Species(id=1, **species.dict())
36    return new_species

Summary

  • ✅ BaseModel - the base class
  • ✅ Automatic type conversion
  • ✅ Field validators (gt, ge, pattern, etc.)
  • ✅ Custom validators (@field_validator)
  • ✅ Safari API with Pydantic

Next lesson: Async databases with SQLAlchemy! 🗄️⚡

Go to CodeWorlds