DynamoDBのORM「PynamoDB」を使ってみる

DynamoDBをORMのように操作できるPythonのパッケージ「PynamoDB」を使ってみます。辞書化型とクラスの変換をする必要がなくなり、楽にDBを操作できます。
2021.10.27

PynamoDB

PynamoDBはDynamoDBのPython用クライアントです。 SQLAlchemyなどのORMのようにDynamoDBを操作することができます。 Boto3などを使ってDynamoDBを操作していると、dict⇄データクラスの変換が発生してコードが冗長になる気がします。 また、dict型だとインテリセンスの問題もあります。クラスになっているとその辺りは楽になる気がします。

インストール

pipでインストール可能です

インストール

pip3 install pynamodb

使ってみる

テーブルの定義

テーブルの定義

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


class User(Model):
    class Meta:
        table_name = "User"
        region = 'ap-northeast-1'
        read_capacity_units = 1
        write_capacity_units = 1

    name = UnicodeAttribute(hash_key=True)
    email = UnicodeAttribute(range_key=True)
    age = NumberAttribute(null=True)

テーブルのデータモデルを定義しています。 内部クラスのMetaにはテーブルの設定を書くことができます。

今回はUserというテーブルを作成しています。 nameをパーティションキーにして、emailをソートキーにしています。 ageは単なるAttributeです。

Attributeとして使えるのは以下のものです。

  • UnicodeAttribute
  • UnicodeSetAttribute
  • NumberAttribute
  • NumberSetAttribute
  • BinaryAttribute
  • BinarySetAttribute
  • UTCDateTimeAttribute
  • BooleanAttribute
  • JSONAttribute
  • MapAttribute

UTCDateTimeなどがあるのは便利です。シリアライズの手間が省けます。

自分でAttributeを定義することもできます。その場合はシリアライズ、デシリアライズを実装するだけで定義可能です。

テーブルをデプロイする

テーブルの作成

if not User.exists():
    User.create_table()

テーブルが存在しない場合に新しく作成します。

削除したい場合は以下のコードで可能です。

テーブルの削除

User.delete_table()

データをPutする

Put

foo = User('foo', 'foo@xxx.xxx', age=100)
foo.age = 5  # この方法でも可能
foo.save()

基本的な流れはインスタンスを作成→保存といった感じです。

たくさんのデータを一度に保存したい場合はBatchWrite用のインターフェイスが用意されています。 また、トランザクション用のインターフェイスもあります。

データを取得する

Get

user = User.get('foo', 'foo@xxx.xxx')
print(f'name: {user.name}')
# -> name: foo

キーを指定することで、データの取得が可能です。 どのAttributeをGetするかなどの指定も可能です。 クエリを使用して条件に当てはまるものをイテレーターとして受け取ることも可能です。

データを更新する

Update

user.update(actions=[
    User.age.add(1)
])

print(f'age: {user.age}')
# -> age: 6

actionsにDynamoDBでの更新の操作に対応するものを入れることができます。 各AttributeにはDynamoDBのオペレータに対応するメソッドがついてます。 ここではADDに対応するaddを使用して1を足しています。 他にもSETに対応するsetなどすべての操作が用意されています。

データの削除

Delete

user.delete()

deleteメソッドを呼べば該当インスタンスのアイテムを削除できます。

最後に

今まではデータクラスを定義して、それにシリアライズ等の処理を書いて、クエリを書いて、といったような処理を何度も書いてました。 プロジェクトの規模によっては抽象化することもありましたが、面倒な作業でした。 PynamoDBを使えば同様のことが少ない手間でできるので便利だと思います。 また、ローレベルのAPIも用意されているので、細かいチューニングにも対応できると思います。