雰囲気データサイエンティストの備忘録

Atmosphere Data Scientist's Memorandum


Django REST FrameworkでAPIを作る
Python
Django

RESTfull API

  • 参考
    https://aws.amazon.com/jp/what-is/restful-api/

  • RESTfull APIの4つの原則

    • URIで公開されている
    • HTTPメソッドによりリクエストを受け付けるインターフェースを持つ
    • ステートレスな処理実行
    • 処理の結果をHTTPステータスコードに変換して応答する

Django Rest Framework(DRF)

DjangoでRestAPIを作成できるフレームワークとして,Django Rest Framework(DRF)があります。
端折っていますが,下の3つのファイルが重要です。

  • models.py
    データモデルを定義するファイル。ユーザーから提供されたデータをDBに格納したり,DBのテーブルに対して直接クエリを行うような処理を実装する
  • views.py
    HTTPメソッド(GET、POST、PUT、DELETEなど)に基づいて,modelsに対して問い合わせを行う
  • serializers.py:
    データのバリデーションや変換(json形式)などを行う

ユーザーから見ると,viewserializermodelDBの順にアクセスすることになります。

テーブルの操作

適当な題材として,パンのレビューを収集するBreadアプリを作ります。
https://github.com/nm-dl1212/django_restapihttps://github.com/nm-dl1212/django_restapi

ユーザーが入力する項目は以下の4つとします。

  • 名前
  • 税抜き価格
  • 説明
  • 評価(うまい/そこそこ/まずいの3段階)

新規オブジェクトが追加されたタイミングで,以下の3つの項目をテーブルに追加します。

  • 新規追加された時間
  • 更新された時間
  • 税込み価格

ここで, 「税込み価格」は入力値の「税抜き価格」から自動で計算されるようにします。
今回は単純な掛け算ですが,今後,入力値に対して複雑な処理を実行できるようにすることを意図しています。

model

bread/models.py
from django.db import models


class Bread(models.Model):

    # ユーザーに入力させる項目
    name = models.CharField(max_length=30)
    price = models.IntegerField()
    description = models.CharField(max_length=500)
    
    REVIEW_SET = (
            ("Good", "うまい"),
            ("Soso", "そこそこ"),
            ("Bad", "まずい"),
    )
    review = models.CharField(choices=REVIEW_SET, max_length=8)
    
    # 自動追加される項目
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    
    # 計算式を追加
    @property
    def priceWithTax(self):
        return int(self.price * 1.1)
    
    def __str__(self):
        return self.name

入力の型に合わせて複数のmodels.**Fieldクラスが用意されており,ここで入力のバリデーションを行います。
税抜き価格priceに定数を書けて税込み価格priceWithTaxを返却するプロパティを実装します。

view

bread/views.py
from rest_framework import viewsets
from .models import Bread
from .serializers import BreadSerializer


class BreadViewSet(viewsets.ModelViewSet):
    queryset = Bread.objects.all()
    serializer_class = BreadSerializer

BreadViewSetは,ModelViewSetクラスを継承しています。
django公式(https://www.django-rest-framework.org/api-guide/viewsets/#modelviewset)にあるように,
ModelViewSetはデータベースへのCRUD処理のメソッドを持っており,それを継承したBreadViewSetでも同様の操作が可能になります!

The actions provided by the ModelViewSet class are .list(), .retrieve(), .create(), .update(), .partial_update(), and .destroy().

querysetでは,Modelのどの項目を取得するかを定義しています。ここではBreadのすべてを対象にするので,Bread.objects.all()とします。
また,ここで次節のSerializerのクラスを指定します。

serializer

product/serializers.py
from rest_framework import serializers
from .models import Bread


class BreadSerializer(serializers.ModelSerializer):
    class Meta:
        model = Bread
        fields = ['name', 'price', 'description', 'review', 'priceWithTax'] # GET時に渡す項目
        read_only_fields = ['created_at', 'updated_at']  # 読み取り専用

serializerはmodelとview間でデータを変換する役割を持ちます。
下記のようにmodelの中ではインスタンスとしてデータを保持しており,これをviewに渡すときにjsonオブジェクトに変換する必要があります。
この時,model→viewへの変換をシリアライズ,view→modelへの変換をデシリアライズと呼びます。

jsonオブジェクトとmodelインスタンス間の仲介をserializerに担当させることで,GETやPOSTなどのリクエストに文字列ベースのインプットを付与することが可能になります。

# modelの中でのデータの扱い
instance = Model(name='John', age=25, sex='male')
# viewの中でのデータの扱い
{
    "name": "John Doe",
    "age": 25
}

実際にDBにクエリを投げる

まず,以下のようにPOSTを投げてみます。
Breadモデルなので,パンのレビューを投げてみました。

$ curl -X POST -H "Content-Type: application/json" -d \
    '{"name": "あんぱん", "price": 120, "description": "あんこたっぷり", "review": "Good"}' \
    http://localhost:8000/bread/bread/

続けて,GETで登録されているオブジェクトを拾ってみます。先ほどの登録したオブジェクトが返ってきますが,priceWithTaxがDjango側で計算され追記されているのがポイントになります。

$ curl -X GET http://localhost:8000/bread/bread/

# [{"name":"あんぱん","price":120,"description":"あんこたっぷり","review":"Good","priceWithTax":132}]

まとめ

Django Rest Frameworkを使って,簡単なDB操作を実行しました。
これから,DBをPostgreSQLに変更してみたり,機械学習のようなもっと複雑なPython処理を実行できるようにブラッシュアップしたいと思います。