Using the AWS SDK with DynamoDB

The AWS SDK for Python is well written, well documented, and makes it relatively easy to work with AWS services. Normally, I enjoy working with the library because it makes coding easier but the DynamoDB portion of the SDK feels verbose and unintuitive. As a result, I was looking for something easier to use and found PynamoDB on Github.

PynamoDB as an alternative to the AWS SDK

If you’re looking for a more Pythonic way to write code for interacting with DynamoDB, PynamoDB is certainly a library to try. From creating tables, to populating and querying them, a lot of the verbosity of the SDK has been abstracted and you can focus on what it is you’re trying to accomplish with the service and less time reading the documentation for the library and service.

Creating a table

To start, create a Model of the table; this is PyanmoDB’s entry point to working with a DynamoDB table. To do this, you extend the Model class and delineate the attributes of the table. Each of the attributes are concrete instances of the Attribute class from pynamodb.attributes. You just need to choose the type(s) that match your table’s attributes. Once the Model is created, the table can be saved. You’ll see examples of all of this below.

The example table used in this post contains product reviews. The ReviewModel class below is a representation of that table using PyanmoDB. The inner Meta class provides table details such as the name (table_name) and other optional attributes such as the host URL. This latter feature can be helpful if you’re testing with a local DynamoDB instance.

The attributes are the name of the product, the url where the product can be found, a numeric rating, and the date it was last rated (last_rating). In this example, name and url form a composite key where the name is the hash portion and url is the range portion.

from pynamodb.attributes import UnicodeAttribute, NumberAttribute, UTCDateTimeAttribute
from pynamodb.models import Model

class ReviewModel(Model):
    class Meta:
        table_name = 'product_reviews'
        host = 'http://localhost:8000'

    name = UnicodeAttribute(hash_key=True)
    url = UnicodeAttribute(range_key=True)
    rating = NumberAttribute()
    last_rating = UTCDateTimeAttribute()
    
ReviewModel.create_table(read_capacity_units=1, write_capacity_units=1)

The attributes described above are of types UnicodeAttribute (name and url), NumberAttribute (rating), and UTCDateTimeAttribute (last_rating). These attribute types are a simple way to declare data types without having to remember or find the character code as you do with the AWS SDK.

Creating a new entry

With the Model declared, you can use it to instantiate new reviews and write them to the newly created table. A new review for cell phone A is created below.

from datetime import datetime

new_review = ReviewModel(name='cell phone A',
                         url='https://www.example.com/abc123',
                         rating=4.5,
                         last_rating=datetime.utcnow())

Once the review is created, it can be written to the table. Instantiating a new entry does not actually write it to the database table; it must be explicitly saved.

import logging
from pynamodb.exceptions import PutError

try:
    new_review.save(ReviewModel.name.does_not_exist())
except PutError as e:
    logging.error('Unable to add new review')

This particular example illustrates the ability to do even more than simply write to the database because it uses a conditional write ReviewModel.name.does_not_exist() so that if an entry with this composite key (name and url) already exists, it won’t be overwritten. The developer guide discusses conditional expressions so review that for more details.

Querying the table

Maybe even easier than creating the table and adding to it is querying it. Normally, you’d need to consider pagination depending on the size of the query results, but the PynamoDB library uses a more straightforward syntax. If we were to query the table created above for a review with the name cell phone A, it would look like the below.

# query
ReviewModel.query('cell phone A')

# return a list from query using list comprehension
[review for review in ReviewModel.query('cell phone A')]

Summary

PynamoDB is an excellent library to try if you’re looking for an alternative to the AWS SDK when interacting with DynamoDB. It provides a more Pythonic interface to the service by using Python classes and methods in ways that are likely more familiar to a Python developer than those provided by the AWS SDK. It still contains the flexibility and features that the SDK provides and allows you to focus on what you’re doing with the service rather than focusing on the service itself.

If you have questions or comments about this post, please leave them below. If you’d like to know when other posts are available, please follow us on Twitter.