3分で始めるDynamoDB-Local by Python

# Amazon DynamoDB をローカル環境で使えます

 - Amazon DynamoDB (以下DynamoDB)の使い方を勉強したい。
 - でも勉強用なのでクラウド料金は気にしたくない。
 - そしてDockerをあまり知らない。

それでも大丈夫！
Docker のツールさえ入っていれば**誰でも簡単に**ローカル環境で DynamoDB を使うことができます。
2018年8月に Amazon が DynamoDB のローカル使用のための Dockerイメージをリリースしています。
Docker について知らなくてもDockerコマンドが使えるツールが入っていればOKです。

 　- [AWS - DockerImageのアナウンス](https://aws.amazon.com/jp/about-aws/whats-new/2018/08/use-amazon-dynamodb-local-more-easily-with-the-new-docker-image/)
　- [DockerHub - amazon/dynamodb-local](https://hub.docker.com/r/amazon/dynamodb-local/)


この記事では、Windows で docker toolbox を使用しています。
この記事では DynamoDB には Python でアクセスしてみます。

# DynamoDB 起動

まずは、Docker Quickstart Terminal を起動します。
**とりあえずDynamoDBのコンテナ起動に関してはこれだけで準備完了。**
早速以下のコマンドでDynamoDBをローカル環境で起動します。

```:コマンド
docker run -d -p 8000:8000 amazon/dynamodb-local
```

# DynamoDB 起動確認
docker psコマンドでDynamoDBが起動されていることを確認します。

```:DockerQuickstartTerminal
$ docker ps
CONTAINER ID        IMAGE                   COMMAND                  CREATED             STATUS              PORTS                    NAMES
5c175b6efca9        amazon/dynamodb-local   "java -jar DynamoDBL…"   14 seconds ago      Up 13 seconds       0.0.0.0:8000->8000/tcp   peaceful_ardinghelli
```
StatusがUpになっていればOK。portは8000です。
**ここまで1分! DynamoDBがローカルで起動されました。**

# DynamoDB に画面からアクセスする

続いて、DynamoDBにアクセスしてみます。
Pythonのコードを準備する前に、手っ取り早く画面で確認できます。
DynamoDBが起動されると　DynamoDB JavaScript Shell という画面が表示できます。
以下のURLでその画面を開くことができます。

```:URL
http://192.168.99.100:8000/shell/
```

IP番号の部分は Docker for Windows だと「localhost」でよかったりします。
上記 IP番号は docker の default machine のIPです。 
Docker Quickstart Terminal 起動時に最初に出てくるクジラの右下に出てるそれです。

```:DockerQuickstartTerminal

                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
           \______ o           __/
             \    \         __/
              \____\_______/

docker is configured to use the default machine with IP 192.168.99.100
For help getting started, check out the docs at https://docs.docker.com
```

それでは http://192.168.99.100:8000/shell/ をブラウザに入力してDynamoDB JavaScript Shellを開きます。

- top画面
![image.png](https://qiita-image-store.s3.amazonaws.com/0/188272/cb5e6914-dde8-382d-cc30-9dd9400d6197.png)
DyanamoDB がうまく起動できていると、左上にAmazon Web Serviceのロゴが入っている画面が表示されます。
右上の「</>」のボタンを押下すると、TABLE操作のAPI一覧が表示され、簡単に実行できたりします。
それ以上の、この画面の操作方法は割愛。

# DynamoDB に Python からアクセスする

さあ、次は Python から DynamoDB へアクセスしてみます。
Python からのアクセスは boto3 というライブラリを使います。
boto3 は Python用 AWS SDK です。

こちらの DynamoDB のページを参考に使いましょう。
- [Boto3 Documentation](https://boto3.amazonaws.com/v1/documentation/api/latest/index.html#)

## 準備

bot3 は pip コマンドで簡単に install できます。

```:コマンド
pip install boto3
```

それではPythonによるテーブル操作について、シンプルなサンプルの Pythonプログラム と実行結果を以下に例示します。

## テーブル作成

まずは、テーブルを作ります。

 - pythonプログラム

```python:create_table.py
import boto3

dynamodb = boto3.resource('dynamodb', endpoint_url="http://192.168.99.100:8000",
                            region_name='us-west-2',
                            aws_access_key_id='ACCESS_ID',
                            aws_secret_access_key='ACCESS_KEY')

table = dynamodb.create_table(
    TableName='users',
    KeySchema=[
        {
            'AttributeName': 'user_id',
            'KeyType': 'HASH'
        }
    ],
    AttributeDefinitions=[
        {
            'AttributeName': 'user_id',
            'AttributeType': 'N'
        }
    ],
    ProvisionedThroughput={
        'ReadCapacityUnits': 5,
        'WriteCapacityUnits': 5
    }
)

print('Table status:', table.table_status)
```
ここで注意！
boto3 で DynamoDB のテーブル操作をするためには、region_name や access_key_id、aws_secret_access_key を設定する必要があります。
設定方法は aws-cliをインストールして aws configure というコマンドで設定する方法もありますが、
ひと手間かかるので、記載例では手っ取り早くPythonプログラムに指定しています。
ローカル環境のDynamoDBにアクセスするうえでは、認証処理はないようで、適当な文字列の指定でアクセスできました。使えるawsアカウントがなくても問題ないのです。

 - 実行＆結果
 
```:ターミナル
>python create_table.py
Table status: ACTIVE
```
テーブル作成がうまくいき、Activeな状態になりました。

## テーブル一覧取得
テーブルが作られたことをテーブル一覧を取得して確認します。
list_tables() というコマンドで取得できます。

 - pythonプログラム
 
```python:list_tables.py
import boto3

dynamodb = boto3.client('dynamodb', endpoint_url="http://192.168.99.100:8000",
                            region_name='us-west-2',
                            aws_access_key_id='ACCESS_ID',
                            aws_secret_access_key='ACCESS_KEY')

tables = dynamodb.list_tables()

print('Tables List:',tables['TableNames'])
```
最初のdynamodbへのアクセス部分が boto3.resource() ではなく boto3.client() であることに注意です。

 - 実行＆結果
 
```:ターミナル
>python list_tables.py
Tables List: ['users']
```
users テーブルが追加されていることが確認できました。

## レコード登録＆参照

レコードを登録して、テーブルの登録したレコードを参照します。
Table.put_item() でレコード登録、Table.get_item() で登録されているレコードを参照します。

- pythonプログラム

```python:put_item.py
import boto3

dynamodb = boto3.resource('dynamodb', endpoint_url="http://192.168.99.100:8000",
                            region_name='us-west-2',
                            aws_access_key_id='ACCESS_ID',
                            aws_secret_access_key='ACCESS_KEY')

table = dynamodb.Table('users')
table.put_item(
    Item={
        'user_id': 1,
        'user_name': 'Buzz',
        'user_age': 30
    }
)

item = table.get_item(Key={'user_id': 1})

print(item['Item'])
```

- 実行＆結果

```:ターミナル
>python put_item.py
{'user_id': Decimal('1'), 'user_age': Decimal('30'), 'user_name': 'Buzz'}
```

レコードが登録されました。
数値は Decimal('xx') と表示されています。

## レコード更新

Table.update_item() で更新できます。
下の記載例では、数値の加算を行っています。
UpdateExpression で ADD のキーワードを使って、該当項目の元の値に加算します。
該当項目がない場合は、新しい項目としてキー追加および加算値を登録します。

- pythonプログラム

```python:update_item.py
import boto3

dynamodb = boto3.resource('dynamodb', endpoint_url="http://192.168.99.100:8000",
                            region_name='us-west-2',
                            aws_access_key_id='ACCESS_ID',
                            aws_secret_access_key='ACCESS_KEY')

table = dynamodb.Table('users')
res = table.update_item(
    Key={
        'user_id': 1
    },
    ExpressionAttributeNames={
        '#age': 'user_age',
        '#level': 'user_level'
    },
    ExpressionAttributeValues={
        ':count': 1,
        ':count2': 5

    },
    UpdateExpression='ADD #age :count, #level :count2',
    ReturnValues="UPDATED_NEW"
)

item = table.get_item(Key={'user_id': 1})

print(item['Item'])
```


- 実行＆結果

```:ターミナル
>python update_item.py
{'user_age': Decimal('31'), 'user_level': Decimal('5'), 'user_id': Decimal('1'), 'user_name': 'Buzz'}
```

Table.update_item() の ADD の処理により、user_age に 1 加算され、user_level が追加されました。


## レコード削除＆スキャン

Table.delete_item() というコマンドでレコードの削除ができます。
レコード削除の確認のために、削除の前後でレコードを全件取得して確認してみます。
Table.scan() というコマンドで全件取得できます。

```python:delete_item.py
import boto3

dynamodb = boto3.resource('dynamodb', endpoint_url="http://192.168.99.100:8000",
                            region_name='us-west-2',
                            aws_access_key_id='ACCESS_ID',
                            aws_secret_access_key='ACCESS_KEY')

table = dynamodb.Table('users')

# 全件取得
items = table.scan()
print(items['Items'])

# レコード削除
table.delete_item(Key={'user_id': 1})
print('delete_item done')

# 全件取得
items = table.scan()
print(items['Items'])
```

- 実行＆結果

```:ターミナル
>python delete_item.py
[{'user_age': Decimal('31'), 'user_level': Decimal('5'), 'user_id': Decimal('1'), 'user_name': 'Buzz'}]
delete_item done
[]
```
delete_item実施後の結果でレコードが削除されていることがわかります。


## テーブル削除

Table.delete() というコマンドで削除できます。
削除されたことの確認のためのテーブル一覧取得も続けて実行するようにします。

- pythonプログラム

```python:delete_table.py
import boto3

# テーブル削除
dynamodb = boto3.resource('dynamodb', endpoint_url="http://192.168.99.100:8000",
                            region_name='us-west-2',
                            aws_access_key_id='ACCESS_ID',
                            aws_secret_access_key='ACCESS_KEY')

table = dynamodb.Table('users')
table.delete()

#テーブル一覧
dynamodb = boto3.client('dynamodb', endpoint_url="http://192.168.99.100:8000",
                            region_name='us-west-2',
                            aws_access_key_id='ACCESS_ID',
                            aws_secret_access_key='ACCESS_KEY')

tables = dynamodb.list_tables()

print('Tables List:',tables['TableNames'])
```

 - 実行＆結果
 
```:ターミナル
>python delete_table.py
Tables List: []
```

users テーブルが削除されました。