Custom Types¶
aiodynamodb ships several Pydantic type annotations in aiodynamodb.custom_types that control how values are serialized to and from DynamoDB.
Timestamp types¶
All timestamp types annotate a datetime field. They differ only in the integer precision stored in DynamoDB as a Number (N).
| Type | DynamoDB storage | Formula |
|---|---|---|
Timestamp |
Unix seconds (int) | int(d.timestamp()) |
TimestampMillis |
Unix milliseconds (int) | int(d.timestamp() * 1_000) |
TimestampMicros |
Unix microseconds (int) | int(d.timestamp() * 1_000_000) |
TimestampNanos |
Unix nanoseconds (int) | int(d.timestamp() * 1_000_000_000) |
Usage¶
from datetime import datetime
from aiodynamodb import DynamoModel, table
from aiodynamodb.custom_types import Timestamp, TimestampMillis
@table("events", hash_key="event_id", range_key="occurred_at")
class Event(DynamoModel):
event_id: str
occurred_at: Timestamp # stored as integer seconds in DynamoDB
processed_at: TimestampMillis | None = None
event = Event(
event_id="e1",
occurred_at=datetime(2026, 1, 1, 12, 0, 0), # stored as 1767254400
processed_at=None,
)
await db.put(event)
As key types¶
Timestamp types work as hash_key or range_key fields:
@table("events", hash_key="event_id", range_key="occurred_at")
class Event(DynamoModel):
event_id: str
occurred_at: Timestamp
When you call db.get() or db.update() with a datetime as the key value, the client serializes it to the correct integer automatically.
JSONStr¶
JSONStr[T] stores a Pydantic model as a JSON string in DynamoDB (a String/S attribute), and deserializes it back transparently on read.
from pydantic import BaseModel
from aiodynamodb.custom_types import JSONStr
class Address(BaseModel):
street: str
city: str
@table("profiles", hash_key="profile_id")
class Profile(DynamoModel):
profile_id: str
address: JSONStr[Address] # stored as '{"street":"...","city":"..."}'
Use this when you want to store nested structured data as a string (for example, to fit within a field that has a string constraint elsewhere, or to avoid DynamoDB Map overhead for deeply nested structures).
KeyT¶
KeyT is the union of all types accepted as hash_key and range_key values in client method calls:
You don't use KeyT in model field annotations — it's the type used by db.get(), db.update(), db.delete(), etc. for their key parameters.
ReturnValues¶
ReturnValues is a literal type for the return_values parameter of db.update():
float and Decimal¶
DynamoDB requires Decimal for numeric values, not Python float. aiodynamodb handles this automatically:
- At
@table()decoration time, the decorator inspects the model's fields and sets_has_float_fields = Trueif any field (recursively, including nested models) has afloatannotation. - When serializing with
to_dynamo_compatible(), if_has_float_fieldsisTrue, allfloatvalues are recursively cast toDecimal(str(value))before being handed to boto3. - If no float fields are present, this traversal is skipped entirely for performance.
This means you can use float naturally in your models without thinking about Decimal: