Django REST Frameworkでdynamodbを操作するREST APIを作成する


はじめに

django REST Frameworkとboto3でAWSのDynamoDBに対して操作を行うapiを作成する。
GET、POST、PUT、DELETEの操作ができるようにする。

Dynamodbテーブル作成(事前準備)

下記のようなテーブルを事前に用意し、いくつかデータを入れておく
テーブル名: Fruits
hash key: Name

djangoプロジェクトの作成

django project(dynamo_operation)とapp(api)を作成

$ django-admin startproject dynamo_operation
$ cd dynamo_operation/
$ django-admin startapp api 

setting.pyの編集

setting.pyrest_frameworkと先ほど作成したappのconfigを追加する。

dynamo_operation/setting.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework', #追加
    'api.apps.ApiConfig', #追加
]

DynamoDBリクエスト用のmodelを作成

djangoではDBの作成、操作にmodelを用意する。
DynamoDBへのリクエストはboto3を使用するので特にmodelは必要ないが、今回はmodel(dynamo_model.py)を用意した。

api/dynamo_model.py
class Fruit():
    def __init__(self, name):
        self.name = name

views.pyの編集

api/views.py
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response 
from api.dynamo_models import Fruit
from boto3 import client

dynamodb_client = client('dynamodb')

class DynamoRequest(APIView):
    # 全体GET
    def get(self, request):
        response = []
        items = dynamodb_client.scan(TableName='Fruits')['Items']
        for item in items:
            fruit = Fruit(item['Name']['S'])
            fruit.price = item.get('Price',{}).get('N', '')
            response.append(fruit.__dict__)
        return Response(response)

    def post(self, request):
        request_data = request.data
        item = {'Name': {'S': request_data['Name']}}

        if 'Price' in request_data:
            item['Price'] = {'N': request_data['Price']}

        dynamodb_client.put_item(
            TableName = 'Fruits',
            Item = item
        )
        return Response(status=status.HTTP_201_CREATED)


class DynamoDetailRequest(APIView):
    #単体GET
    def get(self, request, pk):
        item = dynamodb_client.get_item(
            TableName = 'Fruits',
            Key = {
                'Name': {'S': pk},
            }
        )['Item']
        fruit = Fruit(item['Name']['S'])
        fruit.price = item.get('Price',{}).get('N', '')
        return Response(fruit.__dict__)

    def put(self, request, pk):
        request_data = request.data

        item = dynamodb_client.get_item(
            TableName = 'Fruits',
            Key = {
                'Name': {'S': pk},
            }
        )['Item']

        price = item.get('Price',{}).get('N', '0')

        if 'Price' in request_data:
            price = request_data['Price']

        dynamodb_client.put_item(
            TableName = 'Fruits',
            Item = {
                'Name': {'S': item['Name']['S']},
                'Price': {'N': price}
            }
        )
        return Response(status=status.HTTP_200_OK)

    def delete(self, request, pk):
        dynamodb_client.delete_item(
            TableName = 'Fruits',
            Key = {
                'Name': {'S': pk},
            }
        )
        return Response(status=status.HTTP_204_NO_CONTENT)

rest_frameworkAPIViewを継承したclassでリクエストを処理する。
DynamoRequestがpathパラメータなしのリクエストを処理し、DynamoDetailRequestでpathパラメータ(pk)ありのリクエストの処理を行う。
APIViewを継承することにより、HTTPメソッドごとにfunctionを用意することでそれぞれのメソッドに対応する処理を追加することができる。

urls.pyの編集

api/urls.py
from django.urls import path
from api import views

urlpatterns = [
    path('api/', views.DynamoRequest.as_view()),
    path('api/<pk>/', views.DynamoDetailRequest.as_view())
]

dynamo_oprationフォルダのurls.pyも編集する

dynamo_opration/urls.py
from django.urls import path, include

urlpatterns = [
    path('', include('api.urls')),
]

curlコマンドで動作確認

serverの起動

$ python manage.py runserver

GET(全体検索)

$ curl http://127.0.0.1:8000/api/

# レスポンス
[{"name":"orange","price":"200"},{"name":"banana","price":"100"},{"name":"apple","price":"100"}]

POST

$ curl -X POST \
  -H 'Content-Type:application/json' \
  -d '{"Name": "peach", "Price": "400"}' \
  http://127.0.0.1:8000/api/

POSTリクエスト後のテーブル

peachの項目が追加されている。

GET(単体)

appleの項目を取得

$ curl http://127.0.0.1:8000/api/apple/

# レスポンス
{"name":"apple","price":"100"}

PUT

appleのpriceを100 -> 200へ変更する

$ curl -X PUT \
  -H 'Content-Type:application/json' \
  -d '{"Price": "200"}' \
  http://127.0.0.1:8000/api/apple/

PUTリクエスト後のテーブル

DELETE

peachの項目を削除する。

$ curl -X DELETE http://127.0.0.1:8000/api/peach/

DELETEリクエスト後のテーブル

おわりに

django REST Framework + boto3でDynamoDBの操作を行うREST Apiを作成した。
今回は、dynamodb_model.pyを用意してmodelを管理するようにしたが、必要なかったかもしれない(この辺の設計は今後改善していきたい)。