【AWS】PynamoDBを使ってみる

# はじめに

おじさんは、AWSのお勉強しています。先日購入した書籍「動かして学ぶ！Pythonサーバレスアプリ開発入門」 を読んでいたら PynamoDBを使っていたのですが、そこで躓いてしまいました。書籍のソースコードは公開されています。（翔泳社の書籍の案内ページからリンクもあります）。


PynamoDBについて、下記のサイトをざっと読みました。
NoSQLなるもので、従来のrelational DBと異なるそうです。

https://koh0214.hatenablog.com/entry/pynamoDB

# pynamoDBを動かした

## 準備

pynamodb そのものは普通に pip で入ります。

https://pynamodb.readthedocs.io/en/latest/



## DynamoDBLocalの用意

pynamodb は　DynamoDBというサーバと接続して使うもののようで、まずサーバとして動くDBが必要です。

ローカルでDBを動かすためにはいくつか方法がありますが、ここではDynamoDB Local というものを動かしました。

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html


tar ファイルをダウンロードしてjava プログラムを実行します。

```Shell
$ tar xfvz dynamodb_local_latest.tar.gz
DynamoDBLocal_lib/
DynamoDBLocal.jar 
...
$ java -Djava.library.path=./DynamoDBLocal_lib -jar DynamoDBLocal.jar -sharedDb
Initializing DynamoDB Local with the following configuration:
Port:   8000
InMemory:       false
DbPath: null
SharedDb:       true
shouldDelayTransientStatuses:   false
CorsParams:     *
```

実行しているディレクトリに```shared-local-instance.db```
というファイルがDynamoDBLocalを実行しているディレクトリにできたので、どうやらＤＢができたっぽいです。

(おまけ) 実は、最初 Docker image を使えば楽かと思い、こちらを試そうとしました。が、docker-compose の version 3.8の指定に対し、こちらのローカルではdocker-compose の version が　1.16.1 で、Compose 仕様を調べたところ version 3.xには未対応でした。
以下にあるように、docker-compose の定義ファイルを持ってきて docker-compose up のようなので楽だと思いましたが、実際には使えず残念。

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/DynamoDBLocal.DownloadingAndRunning.html


### Model の定義

本題のDBのテーブル作成と属性の追加削除を試しています。

まず ```pynamodb.models.Model``` を継承したModel を使って定義し、```pynamodb.attributes.Attribute```　を継承したクラスの属性を定義します。テキストにはUnicodeAttribute, 時刻にはUTCDateTimeAttribute などが用意されています。

```Python:mymodel.py
from pynamodb.models import Model
from pynamodb.attributes import (
    UnicodeAttribute, NumberAttribute, UnicodeSetAttribute, UTCDateTimeAttribute
)

from datetime import datetime


class MyInheritedModel(Model):
    class Meta:
        table_name = 'Thread'  
        region = 'us-west-1' # Specifies the region
        # Optional: Specify the hostname only if it needs to be changed from the default AWS setting
        host = 'http://localhost'
        write_capacity_units = 10
        read_capacity_units = 10

    textMsg = UnicodeAttribute(hash_key=True)
    views = NumberAttribute(default=0)
    tags = UnicodeSetAttribute()
    isChecked = BooleanAttribute(null=False, default=False)
    createdAt = UTCDateTimeAttribute(null=False, default=datetime.now())
```

### テーブル作成

定義したモデルのテーブルを作成します。AWSでなくローカルで動かすときはhost を指定します。引数にwait を指定すると、テーブル作成が終了するまでblock されます。

```Python
from mymodel import MyInheritedModel
if not MyInheritedModel.exists():
    MyInheritedModel.create_table(read_capacity_units=2, write_capacity_units=2, wait=True)
```

### DBに登録

いよいよDBを使ってみます。

- 登録はclass のインスタンスを作って ```.save()```それば良いようです。
- 登録要素の取得は、はmodel に対して```.get()``` で取得できました。query の使い方は別にあり、奥が深いようです。
- 全ての要素を触るには ```.sacan()``` でできました。DBをCSVに保存したいなー、と思ったのですが、方法が分からなかったので、これを使いました。
scan() で全てを参照するiterator を取得できるようなので、一つ一つ書き足していくことはできました。


```Python:item_test.py
from mymodel import MyInheritedModel
from datetime import datetime

for i in range(10):
    msg = f"message{i}"
    myitem = MyInheritedModel(textMsg=msg, tags="v0")
    myitem.save()
    print(f"add msg={msg}, db count={MyInheritedModel.count()}")


try:
    key_msg = "message5"
    itm = MyInheritedModel.get(key_msg)
    print(f"{key_msg} is created at {itm.createdAt}")
except MyInheritedModel.DoesNotExist as e:
    print(e)

scaned = MyInheritedModel.scan()
for itm in scaned:
    print(itm.textMsg, itm.createdAt)

```

実行結果です。

```
$ python3 item_test.py
add msg=message0, db count=10
add msg=message1, db count=10
add msg=message2, db count=10
add msg=message3, db count=10
add msg=message4, db count=10
add msg=message5, db count=10
add msg=message6, db count=10
add msg=message7, db count=10
add msg=message8, db count=10
add msg=message9, db count=10
message5 is created at 2021-10-24 19:15:51.752361+00:00
message2 2021-10-24 19:15:51.752361+00:00
message6 2021-10-24 19:15:51.752361+00:00
message5 2021-10-24 19:15:51.752361+00:00
message8 2021-10-24 19:15:51.752361+00:00
message4 2021-10-24 19:15:51.752361+00:00
message0 2021-10-24 19:15:51.752361+00:00
message1 2021-10-24 19:15:51.752361+00:00
message3 2021-10-24 19:15:51.752361+00:00
message9 2021-10-24 19:15:51.752361+00:00
message7 2021-10-24 19:15:51.752361+00:00
```


### Logging

python の logging が使えます。

https://pynamodb.readthedocs.io/en/latest/logging.html

```Python
import logging
logging.basicConfig()
log = logging.getLogger("pynamodb")
log.setLevel(logging.DEBUG)
log.propagate = True
```

# まとめ

とりあえず、ローカルでAWSで使われているDynamoDBと同じ形のDBのサーバを動かすことができた。
モデルを定義しテーブルを作成し、アイテムを登録し、全データを取得できた。

次のステップとしては、

- いよいよAWSで動かす
- query の使い方をマスターする

かな。まだDBまともに使ったことないし。

### 参考情報

https://qiita.com/gzock/items/e0225fd71917c234acce

https://github.com/pynamodb/PynamoDB

(2021/10/25)