Python
AWS
DynamoDB

DynamoDBの高レベルクライアント「PynamoDB」が良かった

Pythonを使ったDynamoDBの操作には、公式SDKのBoto3を使うことが多いかと思います。Boto3で提供されているのは低レベルなクライアントなため、柔軟な操作ができるという利点はありますが、単純な操作でも記述が複雑になりがちで独自ラッパークラスを作っちゃう問題がありました。

最近PynamoDBという高レベルなクライアントライブラリを見つけて、使ってみたら最高にいい感じだったので基本的な使い方をまとめます。

環境

  • macOS 10.12.6
  • python 3.6.1
  • pynamodb 3.2.1

参考

PynamoDBのドキュメントとServerless FrameworkのExampleに「aws-python-rest-api-with-pynamodb」というのがあったのでそれらを参考にしています。

インストール

$ pip install pynamodb

テーブルモデル定義

まずテーブル定義をします。こんな感じでDjangoのDBモデルっぽく書けます。

todo_model.py
from datetime import datetime

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


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

    # テーブル定義
    createdBy = UnicodeAttribute(hash_key=True, null=False)
    createdAt = UTCDateTimeAttribute(range_key=True, null=False, default=datetime.now())
    text = UnicodeAttribute(null=False)
    checked = BooleanAttribute(null=False, default=False)
    updatedAt = UTCDateTimeAttribute(null=False, default=datetime.now())

Metaクラスにテーブルのリソース定義をします。
localstackなどでローカルに起動したDynamoDBに接続する場合はhost = 'http://localhost:8000'と書いておくと指定したホスト・ポートで起動しているDynamoDBが使えるようになります。
テーブル定義の部分では型の定義の他に、ハッシュキーやレンジキーを指定したり、デフォルト値をセットしたり、nullを許すかどうかということが定義できます。

テーブル作成

create_table()メソッドでモデルをもとにテーブルの作成ができます。
構成の設定情報がコードに入ってくるとリソース管理しづらくてイケてないので、これは動作確認などお試しで使うテーブルを作成する時ですね。

todo_app.py
from todo_model import ToDoModel

if not ToDoModel.exists():
    ToDoModel.create_table(wait=True)

exists()でテーブルの有無が取得できます。
wait=Trueを指定するとはテーブル作成が完了するのを待ちます。
テーブルのキャパシティを引数に指定してcreate_table()するとモデルで定義したキャパシティ設定を上書きできます。

todo_app.py
if not ToDoModel.exists():
    ToDoModel.create_table(read_capacity_units=2, write_capacity_units=2, wait=True)

テーブル削除

delete_table()でテーブル削除できます。

todo_app.py
ToDoModel.delete_table()

アイテム作成

まずアイテムオブジェクトを作成し、オブジェクトのsave()メソッドでテーブルに書き込みます。

todo_app.py
a_todo = ToDoModel('Taro', text='Buy books.')

a_todo.save()

最初の2つの引数は自動でハッシュキー、レンジキーの順番で認識されます。レンジキーがないテーブルの場合または今回の例のようにレンジキーにデフォルト値に設定している場合は、いれなくてOKです。

実行するとアイテムが作成されています。
スクリーンショット 2018-03-18 19.35.50.png

アイテムのクエリ検索

ハッシュキーでクエリ検索し、for文でアイテムごとに処理を行っています。

todo_app.py
for item in ToDoModel.query('Taro'):
    print("createdBy:{0}, createdAt:{1}".format(item.createdBy, item.createdAt))
# createdBy:Taro, createdAt:2018-03-18 19:35:43.531462+00:00
# createdBy:Taro, createdAt:2018-03-18 19:37:00.568644+00:00
# createdBy:Taro, createdAt:2018-03-18 19:37:50.249173+00:00

レンジキーを合わせたクエリ

todo_app.py
for item in ToDoModel.query('Taro', ToDoModel.createdAt > datetime(2018, 3, 18, 19, 37)):
    print("createdBy:{0}, createdAt:{1}".format(item.createdBy, item.createdAt))
# createdBy:Taro, createdAt:2018-03-18 19:37:00.568644+00:00
# createdBy:Taro, createdAt:2018-03-18 19:37:50.249173+00:00

アンド条件を使ったクエリ

todo_app.py
for item in ToDoModel.query('Taro', (ToDoModel.checked == False) & (ToDoModel.text.contains('book'))):
    ...

キーを指定したアイテムの取得

ToDoModel.get()の引数にハッシュキー(とレンジキー)を指定することでアイテムオブジェクトとして取得できます。

todo_app.py
a_todo = ToDoModel.get('Taro', datetime(2018, 3, 18, 19, 35, 43, 531462))
print(a_todo.text)
# Buy books.

キーあたりのアイテム数取得

todo_app.py
print(ToDoModel.count('Taro'))
# 12

アイテムの更新

update()の引数にactionリストで複数項目の更新ができます。

todo_app.py
a_todo = ToDoModel.get('Taro', datetime(2018, 3, 18, 19, 35, 43, 531462))

a_todo.update(actions=[
    ToDoModel.text.set('Go to the library.')
])

print(a_todo.text)
# Go to the library.

スクリーンショット 2018-03-18 19.46.57.png
更新されています。

アイテムの削除

todo_app.py
a_todo.delete()

条件付き操作

todo_app.py
a_todo.delete(ToDoModel.checked == True) # a_todo の checked が Trueだった場合アイテムを削除

条件の記述方法はドキュメントのCondition Expressionsに記載されています。

まとめ

裏でどんなクエリを送信しているか見えづらいので、実際にプロダクション等で使うときはそこまで気にして使う必要があると思います。
アップデートも活発なようで今後も使っていきたい所存。