ちょっと話題の記事

PynamoDB使ってみた

PynamoDBというシンプルにDynamoDBの操作ができるライブラリを見つけたので試しに使ってみました。
2019.08.31

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

PynamoDBというシンプルにDynamoDBの操作ができるライブラリを見つけたので試しに使ってみました。

概要

以下公式ドキュメントからの引用(和訳)です。

PynamoDBは、AmazonのDynamoDBへのPythonインターフェイスです。 PynamoDBでは、DynamoDB APIをシンプルかつ強力に抽象化することで、すぐに開発を開始できます。

PynamoDB is a Pythonic interface to Amazon’s DynamoDB. By using simple, yet powerful abstractions over the DynamoDB API, PynamoDB allows you to start developing immediately.

インストール

$ pip install pynamodb

2019/08/31現在、最新バージョンは4.0.0です。

データモデルの定義

まずは pynamodb.models.Model を継承するモデルクラスを定義します。

from pynamodb.models import Model
from pynamodb.attributes import UnicodeAttribute

class UserModel(Model):
    class Meta:
        table_name = "Devio-user"
        region = 'ap-northeast-1'
    email = UnicodeAttribute(null=True)
    first_name = UnicodeAttribute(range_key=True)
    last_name = UnicodeAttribute(hash_key=True)

UnicodeAttribute の引数に hash_keyrange_key を指定することができます。

DynamoDBは空の値を代入することはできません。
NULL型はサポートしているので、PynamoDBでもnullを許容するかどうかモデルの定義内で指定できます。
nullを許容する属性には引数に null=True を指定しましょう。

テーブル作成

if not UserModel.exists():
    UserModel.create_table(read_capacity_units=1, write_capacity_units=1, wait=True)

アイテム作成

user = UserModel("Oka", "Haruna")
user.email = "oka@example.com"
user.save()

1つ目の引数がHash Key, 2つ目がRange Keyに割り当てられます。

アイテム取得

user = UserModel.get("Oka", "Haruna")

consistent_read=true も指定できます。

クエリ

for user in UserModel.query("Oka", UserModel.first_name.startswith("H")):
    print(user.first_name)
for user in UserModel.query("Haruna", UserModel.email=="oka@example.com"):
    print(user.first_name)

エラーハンドリング

import logging

try:
    user = UserModel.get("Oka", "Haruna")
    logging.info(user)
except UserModel.DoesNotExist:
    logging.error("User does not exist.")

条件付き書き込み

import logging
from pynamodb.exceptions import PutError
new_user = UserModel(first_name='Kadono',
                    last_name='Takuzo',
                    email='t_kadono@example.com')
try:
    new_user.save(UserModel.last_name.does_not_exist())
except PutError as e:
    logging.error('Unable to add new user.')

アイテム更新

user = UserModel.get("Oka", "Haruna")
user.update(actions=[
    UserModel.hire_date.set('20180501')
])

アイテムの削除

user.delete()

GSI

from pynamodb.models import Model
from pynamodb.indexes import GlobalSecondaryIndex, AllProjection
from pynamodb.attributes import NumberAttribute, UnicodeAttribute

class ViewIndex(GlobalSecondaryIndex):
    class Meta:
        read_capacity_units = 2
        write_capacity_units = 1
        projection = AllProjection()
    view = NumberAttribute(default=0, hash_key=True)

class UserModel(Model):
    class Meta:
        table_name = "Devio-user"
        region = 'ap-northeast-1'
    email = UnicodeAttribute(null=True)
    first_name = UnicodeAttribute(range_key=True)
    last_name = UnicodeAttribute(hash_key=True)
    view = NumberAttribute(default=0)
    view_index = ViewIndex()

DynamoDB localの使用

モデル定義に host にローカルサーバを追加するだけです。

from pynamodb.models import Model
from pynamodb.attributes import UnicodeAttribute

class UserModel(Model):
    """
    A DynamoDB User
    """
    class Meta:
        table_name = "Devio-user"
        host = "http://localhost:8000"
    email = UnicodeAttribute(null=True)
    first_name = UnicodeAttribute(range_key=True)
    last_name = UnicodeAttribute(hash_key=True)

まとめ

個人的な所感ですがBoto3よりも直感的に操作ができる印象でした。
更新も活発なようなので、今後も期待です。

参考

Github PynamoDBドキュメント