Dyntastic
Dyntastic is a Python library that provides an intuitive Object-Document Mapper (ODM) for Amazon DynamoDB, built on top of Pydantic for data validation and `boto3` for AWS interaction. It simplifies schema definition, data serialization, and common DynamoDB operations. The current version is 0.18.0, with minor releases occurring approximately every 1-2 months.
Common errors
-
ImportError: cannot import name 'DyntasticModel' from 'dyntastic.models'
cause The `DyntasticModel` class, which is the base for all Dyntastic models, is directly available from the top-level `dyntastic` package.fixChange the import statement to `from dyntastic import DyntasticModel`. -
pydantic.v1.error_wrappers.ValidationError: field required (type=value_error.missing)
cause This error or similar Pydantic validation issues often arise when there's a mismatch between your Pydantic version and the `dyntastic` version's Pydantic compatibility (e.g., using `dyntastic<0.13.1` with `pydantic>=2.0`).fixIf using `pydantic>=2.0`, ensure `dyntastic>=0.13.1` is installed. If using an older `pydantic` version, ensure `dyntastic` matches its expected Pydantic major version. -
Validation Error: ExpressionAttributeValue specified with no actual value for key: ... (during transaction)
cause This specific validation error related to empty `ExpressionAttributeValue` during a transaction operation was a bug in Dyntastic versions prior to 0.16.0.fixUpgrade `dyntastic` to version `0.16.0` or higher to resolve this transaction-related validation bug. -
botocore.exceptions.ClientError: An error occurred (ValidationException) when calling the DeleteItem operation: The provided key element does not match the schema
cause This error can occur in `dyntastic` versions prior to 0.17.0 when attempting to delete an item from a table where the hash key was defined using a field alias.fixUpgrade `dyntastic` to version `0.17.0` or higher to correctly handle aliased hash keys during delete operations.
Warnings
- breaking Dyntastic versions prior to 0.13.1 had limited or no compatibility with Pydantic V2. If you are using Pydantic V2 (released with Pydantic 2.0+), you must upgrade Dyntastic to version 0.13.1 or newer.
- gotcha Prior to version 0.11.0, the `transaction()` context manager would attempt to commit batch operations even if an exception was raised during its `__exit__`. This behavior was changed in 0.11.0 to prevent commits when an exception occurs.
- gotcha Dyntastic versions prior to 0.17.0 had issues correctly handling field aliases, specifically causing problems with deleting items from tables where the hash key was defined using an alias.
- gotcha For Dyntastic models, the `__table_region__` and `__table_host__` attributes can be implicitly set via `DYNTASTIC_REGION` and `DYNTASTIC_HOST` environment variables, respectively. This feature was introduced in version 0.14.0.
Install
-
pip install dyntastic
Imports
- DyntasticModel
from dyntastic.models import DyntasticModel
from dyntastic import DyntasticModel
Quickstart
import os
from dyntastic import DyntasticModel
# Set environment variable for DynamoDB region (or explicitly define __table_region__)
# For local development, you might set DYNTASTIC_HOST='http://localhost:8000'
os.environ['DYNTASTIC_REGION'] = os.environ.get('DYNTASTIC_REGION', 'us-east-1')
class User(DyntasticModel):
__table_name__ = "DyntasticExampleUsers"
__table_hash_key__ = "user_id"
user_id: str
name: str
email: str
age: int = 0
class Config:
table_create_args = {
"BillingMode": "PAY_PER_REQUEST"
}
# Create table if it doesn't exist (optional, auto-creates on first write by default)
# User.create_table()
# Create a new user
user_data = {"user_id": "user123", "name": "Alice", "email": "alice@example.com", "age": 30}
alice = User(**user_data)
alice.save() # This will create the table if it doesn't exist
print(f"Saved user: {alice}")
# Retrieve a user by their hash key
retrieved_user = User.get("user123")
print(f"Retrieved user: {retrieved_user}")
# Update user
if retrieved_user:
retrieved_user.age = 31
retrieved_user.save()
print(f"Updated user age: {retrieved_user.age}")
# Delete user
if retrieved_user:
retrieved_user.delete()
print(f"Deleted user: {retrieved_user.user_id}")
# Clean up (optional: delete table)
# User.delete_table()