Introduction to Pydantic
Pydantic is a powerful data validation and settings management library for Python. It uses Python type annotations to define data models, making it an excellent choice for FastAPI applications. Let's dive into how Pydantic can supercharge your FastAPI projects!
Why Use Pydantic?
Pydantic offers several advantages:
- Type safety
- Automatic data validation
- Easy serialization and deserialization
- Seamless integration with FastAPI
Creating Your First Pydantic Model
Let's start with a simple example:
from pydantic import BaseModel class User(BaseModel): name: str age: int email: str
This User
model defines three fields with their respective types. Pydantic will automatically validate data against this model.
Validating Data
Using our User
model:
user_data = { "name": "John Doe", "age": 30, "email": "john@example.com" } user = User(**user_data) print(user)
Pydantic will create a User
instance, validating the data in the process. If any field is missing or of the wrong type, Pydantic will raise a ValidationError
.
Advanced Validation
Pydantic offers more advanced validation features:
Field Constraints
from pydantic import BaseModel, Field class Product(BaseModel): name: str = Field(..., min_length=1, max_length=50) price: float = Field(..., gt=0) quantity: int = Field(..., ge=0)
Here, we use Field
to add constraints to our model fields.
Custom Validators
You can create custom validators for more complex validation logic:
from pydantic import BaseModel, validator class Order(BaseModel): items: list[str] total: float @validator('total') def check_total(cls, v, values): if v <= 0: raise ValueError('Total must be greater than zero') return v
Pydantic and FastAPI
FastAPI uses Pydantic models for request and response handling. Here's a simple example:
from fastapi import FastAPI from pydantic import BaseModel app = FastAPI() class Item(BaseModel): name: str price: float @app.post("/items/") async def create_item(item: Item): return {"item_name": item.name, "item_price": item.price}
FastAPI will automatically validate incoming requests against the Item
model and generate OpenAPI documentation.
Optional Fields and Default Values
Pydantic allows you to define optional fields and default values:
from typing import Optional from pydantic import BaseModel class UserProfile(BaseModel): username: str bio: Optional[str] = None age: int = 18
Nested Models
You can create complex data structures using nested models:
class Address(BaseModel): street: str city: str country: str class User(BaseModel): name: str address: Address
Working with JSON
Pydantic makes it easy to work with JSON data:
user_json = '{"name": "Alice", "address": {"street": "123 Main St", "city": "Wonderland", "country": "Fantasyland"}}' user = User.parse_raw(user_json)
Config and Behaviors
You can customize model behaviors using the Config
class:
class User(BaseModel): name: str age: int class Config: allow_mutation = False extra = 'forbid'
This configuration makes the model immutable and forbids extra fields.
Pydantic Fields for Enhanced Validation
Pydantic's Field
function allows for more detailed field definitions:
from pydantic import BaseModel, Field class Product(BaseModel): name: str = Field(..., description="The name of the product") price: float = Field(..., gt=0, description="The price of the product") tags: list[str] = Field(default_factory=list, max_items=5)
Error Handling
When validation fails, Pydantic raises a ValidationError
. You can catch and handle these errors to provide meaningful feedback:
from pydantic import ValidationError try: user = User(name="John", age="not an integer") except ValidationError as e: print(e.json())
Best Practices
- Use type hints consistently
- Leverage Pydantic's built-in validators
- Create reusable base models
- Use nested models for complex data structures
- Take advantage of Pydantic's config options
By incorporating these Pydantic techniques into your FastAPI projects, you'll create more robust, type-safe, and self-documenting APIs. Happy coding!