PynamoDB Attributes
pynamodb-attributes provides common custom attribute types for PynamoDB models, extending the built-in attributes with specialized types like Timestamp, Timedelta, and Protobuf Enum attributes. It aims to simplify handling complex data types in DynamoDB. The current version is 0.5.1 and it generally follows the release cadence of its upstream dependency, PynamoDB.
Common errors
-
AttributeError: module 'pynamodb.attributes' has no attribute 'TimestampAttribute'
cause Attempting to import custom attributes from `pynamodb.attributes` instead of `pynamodb_attributes`.fixChange your import statement from `from pynamodb.attributes import TimestampAttribute` to `from pynamodb_attributes import TimestampAttribute`. -
TypeError: argument of type 'float' is not iterable (for IntegerSetAttribute) or TypeError: 'float' object cannot be interpreted as an integer (for IntegerAttribute)
cause Attempting to assign a float value or a set containing floats to an `IntegerSetAttribute` or `IntegerAttribute` which expects strict integer types.fixEnsure all values passed to `IntegerSetAttribute` or `IntegerAttribute` are Python `int` types. Explicitly cast floats to integers if needed (e.g., `int(value)`). -
pynamodb.exceptions.PynamoDBException: The provided attribute value is not of type N (for attribute: 'created_at')
cause You are attempting to store a non-numeric type (e.g., a string) into an attribute that expects a Number type in DynamoDB, such as `TimestampAttribute` or `TimedeltaAttribute`.fixEnsure you are passing a Python `datetime.datetime` object to `TimestampAttribute` or a `datetime.timedelta` object to `TimedeltaAttribute`. These attributes handle the conversion to a numeric type automatically.
Warnings
- breaking PynamoDB-attributes versions are tied to PynamoDB major versions. For instance, `pynamodb-attributes==0.3.0` introduced compatibility with `pynamodb==5.0.0`. Upgrading PynamoDB might require upgrading `pynamodb-attributes` to a compatible version.
- gotcha Be aware of the underlying storage format for custom attributes. For example, `TimestampAttribute` stores `datetime` objects as Unix timestamps (numbers), and `TimedeltaAttribute` variants store `timedelta` objects as integers (seconds, milliseconds, or microseconds). These are not stored as ISO 8601 strings.
- gotcha When working with `IntegerSetAttribute`, ensure all values are integers. PynamoDB's built-in `NumberSetAttribute` allows floats, but `IntegerSetAttribute` strictly enforces integers, which can lead to `TypeError` if floats are accidentally used.
- deprecated The `prefix` argument for `UnicodeProtobufEnumAttribute` was made optional in version 0.5.1. While still functional, relying on it being mandatory might lead to confusion if new code omits it.
Install
-
pip install pynamodb-attributes
Imports
- TimestampAttribute
from pynamodb.attributes import TimestampAttribute
from pynamodb_attributes import TimestampAttribute
- TimedeltaMsAttribute
from pynamodb_attributes import TimedeltaMsAttribute
- IntegerSetAttribute
from pynamodb_attributes import IntegerSetAttribute
- UnicodeProtobufEnumAttribute
from pynamodb_attributes import UnicodeProtobufEnumAttribute
Quickstart
from pynamodb.models import Model
from pynamodb.attributes import UnicodeAttribute
from pynamodb_attributes import TimestampAttribute, TimedeltaMsAttribute, IntegerSetAttribute
import datetime
# Configure PynamoDB (for local DynamoDB for example)
# from pynamodb.connection import Connection
# Connection.tables = {}
class MyModel(Model):
class Meta:
table_name = 'my_test_model_table'
region = 'us-east-1'
# host = 'http://localhost:8000' # Uncomment for local DynamoDB
read_capacity_units = 1
write_capacity_units = 1
id = UnicodeAttribute(hash_key=True)
created_at = TimestampAttribute(null=True)
processing_time = TimedeltaMsAttribute(null=True)
favorite_numbers = IntegerSetAttribute(null=True)
# Example Usage:
if __name__ == '__main__':
# MyModel.create_table(wait=True) # Uncomment to create table
item = MyModel(
id='item-123',
created_at=datetime.datetime.now(datetime.timezone.utc),
processing_time=datetime.timedelta(seconds=5, milliseconds=250),
favorite_numbers={1, 5, 10}
)
# item.save() # Uncomment to save item
# retrieved_item = MyModel.get('item-123') # Uncomment to retrieve item
# print(f"Retrieved: {retrieved_item.id}, Created: {retrieved_item.created_at}, "
# f"Processed in: {retrieved_item.processing_time}, Favorites: {retrieved_item.favorite_numbers}")