Models Guide¶
Define data models with validation, indexes, and unique constraints.
Basic Model¶
All models inherit from BaseS3Model:
from s3verless.core.base import BaseS3Model
class Product(BaseS3Model):
name: str
price: float
description: str | None = None
Every model automatically gets:
- id: UUID - Unique identifier
- created_at: datetime - Creation timestamp
- updated_at: datetime - Last modification
Class Configuration¶
Plural Name¶
Controls the API route and S3 prefix:
Default: lowercase class name.
Unique Fields¶
Enforce uniqueness across all instances:
Unique validation runs on create and update.
Indexed Fields¶
Mark fields for optimized queries:
Disable Auto API¶
Exclude model from auto-generated routes:
Field Types¶
Use standard Python types with Pydantic validation:
from datetime import datetime
from decimal import Decimal
from enum import Enum
from uuid import UUID
class Status(str, Enum):
DRAFT = "draft"
PUBLISHED = "published"
class Article(BaseS3Model):
title: str # Required string
content: str | None = None # Optional string
views: int = 0 # Integer with default
price: Decimal # Exact decimal
rating: float # Floating point
is_featured: bool = False # Boolean
tags: list[str] = [] # List of strings
metadata: dict = {} # Dictionary
status: Status = Status.DRAFT # Enum
author_id: UUID # UUID reference
published_at: datetime | None = None # Optional datetime
Validation¶
Use Pydantic validators:
from pydantic import field_validator, EmailStr
class User(BaseS3Model):
email: EmailStr # Built-in email validation
age: int
@field_validator("age")
@classmethod
def validate_age(cls, v):
if v < 0 or v > 150:
raise ValueError("Age must be between 0 and 150")
return v
Computed Properties¶
Add read-only computed fields:
class Order(BaseS3Model):
items: list[dict]
tax_rate: float = 0.1
@property
def subtotal(self) -> float:
return sum(item["price"] * item["quantity"] for item in self.items)
@property
def total(self) -> float:
return self.subtotal * (1 + self.tax_rate)
Model Methods¶
Add custom methods:
class Task(BaseS3Model):
title: str
completed: bool = False
completed_at: datetime | None = None
def mark_complete(self) -> None:
self.completed = True
self.completed_at = datetime.now(timezone.utc)
self.touch() # Update updated_at
S3 Storage¶
Models are stored as JSON in S3:
bucket/
└── data/
└── products/
├── 550e8400-e29b-41d4-a716-446655440000.json
└── 6ba7b810-9dad-11d1-80b4-00c04fd430c8.json
Each file contains:
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"created_at": "2024-01-15T10:30:00Z",
"updated_at": "2024-01-15T10:30:00Z",
"name": "Widget",
"price": 29.99
}
Working with Models¶
Create¶
from s3verless.core.service import S3DataService
service = S3DataService(Product, "my-bucket")
product = await service.create(s3_client, Product(
name="Widget",
price=29.99,
))
Read¶
Update¶
Delete¶
List¶
Best Practices¶
- Keep models small - S3 reads entire objects, so smaller is faster
- Use indexed fields - Mark frequently filtered fields
- Avoid nested objects - Flatten when possible for better query performance
- Use enums for status - Type safety and validation
- Add timestamps - Already included, but add custom ones as needed