Attribute types
Attributes define the fields in your model. Each attribute maps to a DynamoDB type.
Overview
| Type | DynamoDB | Python | Use case |
|---|---|---|---|
StringAttribute |
S | str | Text, IDs, keys |
NumberAttribute |
N | int, float | Counts, prices |
BooleanAttribute |
BOOL | bool | Flags |
BinaryAttribute |
B | bytes | Files, images |
ListAttribute |
L | list | Ordered items |
MapAttribute |
M | dict | Nested objects |
JSONAttribute |
S | dict, list | Complex JSON |
EnumAttribute |
S | Enum | Status, types |
DatetimeAttribute |
S | datetime | Timestamps (ISO) |
TTLAttribute |
N | datetime | Auto-expiring items |
StringSetAttribute |
SS | set[str] | Unique strings |
NumberSetAttribute |
NS | set[int|float] | Unique numbers |
CompressedAttribute |
S | str | Large text |
EncryptedAttribute |
S | str | Sensitive data |
S3Attribute |
M | S3Value | Large files in S3 |
Common parameters
All attributes share these parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
partition_key |
bool | False | Is this the partition key |
sort_key |
bool | False | Is this the sort key |
default |
Any | None | Default value or AutoGenerate strategy |
required |
bool | False | Field must have a value (not None) |
alias |
str | None | None | DynamoDB attribute name (saves storage) |
Tip
Use AutoGenerate strategies for automatic ID and timestamp generation. See Auto-generate strategies.
Tip
Use alias to store short names in DynamoDB while keeping readable names in Python. See Field aliases.
Basic types
StringAttribute
Store text values. Most common attribute type.
from pydynox import Model, ModelConfig
from pydynox.attributes import StringAttribute
class User(Model):
model_config = ModelConfig(table="users")
pk = StringAttribute(partition_key=True)
name = StringAttribute()
email = StringAttribute(required=True) # Required
Key templates
Use templates to build composite keys for single-table design:
class User(Model):
model_config = ModelConfig(table="app")
pk = StringAttribute(partition_key=True, template="USER#{user_id}")
sk = StringAttribute(sort_key=True, template="PROFILE")
user_id = StringAttribute()
user = User(user_id="123")
# pk is auto-built as "USER#123"
On Python 3.14+, you can use t-strings (PEP 750) for templates:
class User(Model):
model_config = ModelConfig(table="app")
pk = StringAttribute(partition_key=True, template=t"USER#{user_id}")
sk = StringAttribute(sort_key=True, template=t"PROFILE")
user_id = StringAttribute()
T-strings work the same as regular string templates. The syntax is cleaner and gets IDE support for the placeholders.
NumberAttribute
Store integers and floats. DynamoDB stores all numbers as strings internally.
from pydynox.attributes import NumberAttribute
class Product(Model):
model_config = ModelConfig(table="products")
pk = StringAttribute(partition_key=True)
price = NumberAttribute()
quantity = NumberAttribute(default=0)
BooleanAttribute
Store true/false values.
from pydynox.attributes import BooleanAttribute
class User(Model):
model_config = ModelConfig(table="users")
pk = StringAttribute(partition_key=True)
is_active = BooleanAttribute(default=True)
is_verified = BooleanAttribute(default=False)
BinaryAttribute
Store raw bytes. Useful for small files or binary data.
from pydynox.attributes import BinaryAttribute
class Document(Model):
model_config = ModelConfig(table="documents")
pk = StringAttribute(partition_key=True)
thumbnail = BinaryAttribute()
ListAttribute
Store ordered lists. Can contain mixed types.
from pydynox.attributes import ListAttribute
class Post(Model):
model_config = ModelConfig(table="posts")
pk = StringAttribute(partition_key=True)
tags = ListAttribute(default=[])
comments = ListAttribute()
MapAttribute
Store nested objects as DynamoDB's native Map type.
from pydynox.attributes import MapAttribute
class User(Model):
model_config = ModelConfig(table="users")
pk = StringAttribute(partition_key=True)
address = MapAttribute()
user = User(
pk="USER#1",
address={"street": "123 Main St", "city": "NYC", "zip": "10001"}
)
JSON and enum types
JSONAttribute
Store dict or list as a JSON string. Different from MapAttribute:
MapAttributeuses DynamoDB's native Map typeJSONAttributestores as a string (useful for complex nested structures)
"""JSONAttribute example - store dict/list as JSON string."""
import asyncio
from pydynox import Model, ModelConfig
from pydynox.attributes import JSONAttribute, StringAttribute
class Config(Model):
model_config = ModelConfig(table="configs")
pk = StringAttribute(partition_key=True)
settings = JSONAttribute()
async def main():
# Save a dict
config = Config(
pk="CFG#1",
settings={"theme": "dark", "notifications": True, "max_items": 50},
)
await config.save()
# Stored as '{"theme": "dark", "notifications": true, "max_items": 50}'
# Load it back
loaded = await Config.get(pk="CFG#1")
print(loaded.settings["theme"]) # "dark"
# Works with lists too
config2 = Config(pk="CFG#2", settings=["item1", "item2", "item3"])
await config2.save()
asyncio.run(main())
When to use JSONAttribute over MapAttribute:
- Deep nesting (DynamoDB has limits on nested maps)
- You need to store the exact JSON structure
- Compatibility with other systems expecting JSON
EnumAttribute
Store Python enum as its value. Keeps your code type-safe.
"""EnumAttribute example - store Python enum as string."""
import asyncio
from enum import Enum
from pydynox import Model, ModelConfig
from pydynox.attributes import EnumAttribute, StringAttribute
class Status(Enum):
PENDING = "pending"
ACTIVE = "active"
INACTIVE = "inactive"
class User(Model):
model_config = ModelConfig(table="users")
pk = StringAttribute(partition_key=True)
sk = StringAttribute(sort_key=True)
status = EnumAttribute(Status, default=Status.PENDING)
async def main():
# Create with enum value
user = User(pk="USER#ENUM", sk="PROFILE", status=Status.ACTIVE)
await user.save()
# Stored as "active" in DynamoDB
# Load it back - returns the enum
loaded = await User.get(pk="USER#ENUM", sk="PROFILE")
print(loaded.status) # Status.ACTIVE
print(loaded.status == Status.ACTIVE) # True
# Default value works
user2 = User(pk="USER#ENUM2", sk="PROFILE")
print(user2.status) # Status.PENDING
asyncio.run(main())
| Parameter | Type | Default | Description |
|---|---|---|---|
enum_class |
type[Enum] | Required | The Enum class |
Date and time types
DatetimeAttribute
Store datetime as ISO 8601 string. Sortable as string, good for range queries.
"""DatetimeAttribute example - store datetime as ISO string."""
import asyncio
from datetime import datetime, timezone
from pydynox import Model, ModelConfig
from pydynox.attributes import DatetimeAttribute, StringAttribute
class Event(Model):
model_config = ModelConfig(table="events")
pk = StringAttribute(partition_key=True)
created_at = DatetimeAttribute()
async def main():
# Save with datetime
event = Event(pk="EVT#1", created_at=datetime.now(timezone.utc))
await event.save()
# Stored as "2024-01-15T10:30:00+00:00"
# Load it back - returns datetime object
loaded = await Event.get(pk="EVT#1")
print(loaded.created_at) # datetime object
print(loaded.created_at.year) # 2024
asyncio.run(main())
Naive datetimes (without timezone) are treated as UTC.
TTLAttribute
Store datetime as epoch timestamp for DynamoDB's auto-delete feature.
from pydynox.attributes import TTLAttribute, ExpiresIn
class Session(Model):
model_config = ModelConfig(table="sessions")
pk = StringAttribute(partition_key=True)
expires_at = TTLAttribute()
# Create session that expires in 1 hour
session = Session(pk="SESSION#123", expires_at=ExpiresIn.hours(1))
await session.save()
ExpiresIn helpers:
| Method | Description |
|---|---|
ExpiresIn.seconds(n) |
n seconds from now |
ExpiresIn.minutes(n) |
n minutes from now |
ExpiresIn.hours(n) |
n hours from now |
ExpiresIn.days(n) |
n days from now |
ExpiresIn.weeks(n) |
n weeks from now |
Models with TTLAttribute also get:
is_expired- check if item expiredexpires_in- get time remaining as timedeltaextend_ttl()- extend expiration and save
Tip
See TTL guide for full documentation on expiration checking, extending TTL, and best practices.
Warning
TTL must be enabled on the DynamoDB table. The attribute name must match exactly.
Set types
StringSetAttribute
Store unique strings. DynamoDB native set type (SS).
from pydynox.attributes import StringSetAttribute
class User(Model):
model_config = ModelConfig(table="users")
pk = StringAttribute(partition_key=True)
roles = StringSetAttribute()
user = User(pk="USER#1", roles={"admin", "editor"})
await user.save()
# Check membership
print("admin" in user.roles) # True
NumberSetAttribute
Store unique numbers. DynamoDB native set type (NS).
"""StringSetAttribute and NumberSetAttribute examples."""
import asyncio
from pydynox import Model, ModelConfig
from pydynox.attributes import (
NumberSetAttribute,
StringAttribute,
StringSetAttribute,
)
class User(Model):
model_config = ModelConfig(table="users")
pk = StringAttribute(partition_key=True)
sk = StringAttribute(sort_key=True)
tags = StringSetAttribute()
scores = NumberSetAttribute()
async def main():
# Create with sets
user = User(
pk="USER#SET",
sk="PROFILE",
tags={"admin", "verified", "premium"},
scores={100, 95, 88},
)
await user.save()
# Load it back - returns Python sets
loaded = await User.get(pk="USER#SET", sk="PROFILE")
print(loaded.tags) # {'admin', 'verified', 'premium'}
print(loaded.scores) # {100, 95, 88}
# Check membership
print("admin" in loaded.tags) # True
print(100 in loaded.scores) # True
# Sets don't allow duplicates
user2 = User(pk="USER#SET2", sk="PROFILE", tags={"a", "a", "b"})
print(user2.tags) # {'a', 'b'}
asyncio.run(main())
Note
Empty sets are stored as None. On load, you get an empty Python set.
Special types
CompressedAttribute
Auto-compress large text. Saves storage costs and avoids the 400KB limit.
from pydynox.attributes import CompressedAttribute, CompressionAlgorithm
class Document(Model):
model_config = ModelConfig(table="documents")
pk = StringAttribute(partition_key=True)
body = CompressedAttribute() # Uses zstd by default
logs = CompressedAttribute(algorithm=CompressionAlgorithm.Lz4)
| Parameter | Type | Default | Description |
|---|---|---|---|
algorithm |
CompressionAlgorithm | Zstd | Compression algorithm |
level |
int | 3 | Compression level |
min_size |
int | 100 | Min bytes to compress |
threshold |
float | 0.9 | Only compress if ratio below this |
Algorithms:
| Algorithm | Best for |
|---|---|
Zstd |
Most cases (default) |
Lz4 |
Speed over size |
Gzip |
Compatibility |
EncryptedAttribute
Encrypt sensitive data using AWS KMS.
from pydynox.attributes import EncryptedAttribute, EncryptionMode
class User(Model):
model_config = ModelConfig(table="users")
pk = StringAttribute(partition_key=True)
ssn = EncryptedAttribute(key_id="alias/my-key")
| Parameter | Type | Default | Description |
|---|---|---|---|
key_id |
str | Required | KMS key ID or alias |
mode |
EncryptionMode | ReadWrite | ReadWrite, WriteOnly, or ReadOnly |
region |
str | None | AWS region |
context |
dict | None | Encryption context |
Modes:
| Mode | Encrypt | Decrypt | Use case |
|---|---|---|---|
ReadWrite |
✓ | ✓ | Full access |
WriteOnly |
✓ | ✗ | Ingest service |
ReadOnly |
✗ | ✓ | Report service |
Tip
See Encryption guide for full documentation on modes, encryption context, and how it works.
S3Attribute
Store large files in S3 with metadata in DynamoDB. Use when files exceed DynamoDB's 400KB limit.
"""Basic S3 upload example."""
import asyncio
from pydynox import Model, ModelConfig
from pydynox.attributes import S3Attribute, S3File, StringAttribute
# Define model with S3Attribute
class Document(Model):
model_config = ModelConfig(table="documents")
pk = StringAttribute(partition_key=True)
name = StringAttribute()
content = S3Attribute(bucket="my-bucket", prefix="docs/")
async def main():
# Upload from bytes
doc = Document(pk="DOC#S3", name="report.pdf")
doc.content = S3File(b"PDF content here", name="report.pdf", content_type="application/pdf")
await doc.save()
print(f"Uploaded to: s3://{doc.content.bucket}/{doc.content.key}")
print(f"Size: {doc.content.size} bytes")
print(f"ETag: {doc.content.etag}")
asyncio.run(main())
| Parameter | Type | Default | Description |
|---|---|---|---|
bucket |
str | Required | S3 bucket name |
prefix |
str | "" | Key prefix for files |
region |
str | None | S3 region (inherits from client) |
After upload, access file metadata:
| Property | Type | Description |
|---|---|---|
bucket |
str | S3 bucket name |
key |
str | S3 object key |
size |
int | File size in bytes |
etag |
str | S3 ETag |
content_type |
str | MIME type |
last_modified |
str | Last modified timestamp |
version_id |
str | S3 version ID |
metadata |
dict | User-defined metadata |
Download methods:
| Method | Description |
|---|---|
get_bytes() |
Download to memory |
save_to(path) |
Stream to file |
presigned_url(expires) |
Get presigned URL |
Tip
See S3 attribute guide for full documentation.
Choosing the right type
| Need | Use |
|---|---|
| Simple text | StringAttribute |
| Numbers | NumberAttribute |
| True/false | BooleanAttribute |
| Nested object | MapAttribute |
| Complex JSON | JSONAttribute |
| Type-safe status | EnumAttribute |
| Sortable timestamp | DatetimeAttribute |
| Auto-expiring items | TTLAttribute |
| Unique values | StringSetAttribute / NumberSetAttribute |
| Large text | CompressedAttribute |
| Sensitive data | EncryptedAttribute |
| Large files | S3Attribute |
Next steps
- Field aliases - Short DynamoDB names, readable Python names
- Indexes - Query by non-key attributes with GSIs
- Auto-generate - Generate IDs and timestamps
- Encryption - Field-level encryption with KMS
- S3 attribute - Store large files in S3