Skip to content

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:

  • MapAttribute uses DynamoDB's native Map type
  • JSONAttribute stores 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 expired
  • expires_in - get time remaining as timedelta
  • extend_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