Django Rest Framework で RESTful な APIを作成する


概要

 この記事は初心者の自分がRESTful なAPIとswiftでiPhone向けのクーポン配信サービスを開発した手順を順番に記事にしています。技術要素を1つずつ調べながら実装したため、とても遠回りな実装となっています。

 前回のSwiftのTableViewCellを使ってTableViewを自由にカスタマイズで、データベースで管理するクーポン情報をAPIを通してアプリが取得し、リスト形式で表示するところまで作成しました。

 次はこのAPIをRESTfulなAPIに改造します。改造にあたりコードを大幅に変更するため、一旦GETで全てのクーポン情報をレスポンスするだけのAPIを実装し、その後にリクエストに応じたクーポン情報をレスポンスするよう修正を加えていきます。

参考

環境

Mac OS 10.15
VSCode 1.39.2
pipenv 2018.11.26
Python 3.7.4
Django 2.2.6

手順

  • Django Rest Framework をインストールする
  • Django Rest Framework を取り込む
  • Serializerを定義する
  • View.pyを改造する
  • URL_patternを定義する
  • curl コマンドを使って動作確認をする

Django Rest Framework をインストールする

自分はpipenvでpythonのプロジェクト(仮想環境)を作っているので、そこに django rest framework をインストールします。pipenvのシェルに入り、インストールのコマンドを実行します。

$ pipenv shell  #シェルに入る
$ pipenv install djangorestframework # インストール実行

インストール中のターミナル...

インストール完了後にPipfileの中を見ると、[packages]djangorestframeworkが追加されています。


[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
django = "*"
djangorestframework = "*"

[requires]
python_version = "3.7"

Django Rest Framework を取り込む

プロジェクト名のディレクトリ配下のsetting.pyINSTALLED_APPS = { に インストールした rest_framework を追加するだけです。


INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'coupon',
    'rest_framework', # 追加
]

これで Django Rest Framework を使う準備が完了しました。

Serializerを定義する

 Rest Framework を使うには Serializer というモジュールが必要です。アプリのディレクトリ配下に自分で Serializer のファイルを作り実装します。

下記のように実装しました。

coupon/serializer.py

from rest_framework import serializers # Django Rest Frameworkをインポート
from .models import Coupon # models.py のcouponクラスをインポート

class CouponSerializer(serializers.ModelSerializer):
    class Meta:
        model = Coupon # 扱う対象のモデル名を設定する
        fields = '__all__'

上のコードで、fieldsはレスポンスしたいモデルフィールド(ここではクーポン情報)を指定します。特に指定せず全ての項目をレスポンスする場合は'__all__'とします。

View.pyを改造する

views.pyを Django Rest Framework を使った場合に合わせて改造します。

  • rest_framework の viewsets と filters をインポート
  • 上で定義した serializer の CouponSerializer をインポート
  • 全てのクーポン情報をレスポンスする queryset に変更
  • rest_framework を使うと データをjson 形式にしたり dump したり、HTTPでレスポンスする処理は自動でやってくれるようなので、それらの不要になった処理を削除
  • 上で定義した serializer.pyのCouponSerializer を呼ぶ。

コードは下記のようになります。非常にシンプルになりました。

views.py
from django.shortcuts import render
from .models import Coupon
from rest_framework import viewsets, filters
from .serializer import CouponSerializer


class CouponViewSet(viewsets.ModelViewSet):
    queryset = Coupon.objects.all() # 全てのデータを取得
    serializer_class = CouponSerializer

URL_patternを定義する

 ami_coupon_api/urls.pyと、coupon/urls.pyを編集します。実感としてはcoupon/urls.pyから編集した方が良さそうです。

coupon/urls.pyの編集内容は下記の通りです。

  • rest_framework の routers をインポート
  • views.py で定義した、CouponViewSet をインポート
  • router を定義して、router.register に URLとそのURLがリクエストされた時に呼び出すview.pyのクラス(ここでは CouponViewSet)を紐付ける。
coupon/urls.py
from django.urls import path
from . import views
from rest_framework import routers
from .views import CouponViewSet

router = routers.DefaultRouter()
router.register(r'coupons', CouponViewSet)

上記で、r’coupons’ の ‘coupons’ の部分が リクエストURLの後ろに付きます。

ami_coupon_api/urls.pyの編集内容は下記の通りです。

  • django.conf.urls の url と include をインポート
  • coupon/urls.py で定義した router を coupon_routerとしてインポート
  • urlpatternsにURLと呼び出し先を定義
  • URLが admin/ の場合は、django の コンソールへ進むように設定
  • api/ の場合は、router で設定したオブジェクトへ進むように設定
ami_coupon_api/urls.py
from django.contrib import admin
from django.urls import path,include
from django.conf.urls import url, include
from coupon.urls import router as coupon_router

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^api/', include(coupon_router.urls)),
]

curl コマンドを使って動作確認をする

 新しいターミナルを開いて GET、POST、PUT のリクエストを試してみます。リクエストの書き方は下記の通りです。-X はHTTPメソッド(GET、POST、PUT など)を指定するためのオプションです。なお、GETとPOST は指定しなくても実行可能でした。

$ curl -X [HTTPメソッド] [URL] [リクエストパラメータ]

GETを試します。リクエストパラメータは付けていません。

$ curl -X GET http://127.0.0.1:8000/api/coupons/

こんなjsonが返ってきました。


[{"id":1,"code":"0001","benefit":"お会計から1,000円割引","explanation":"5,000円以上ご利用のお客様限定。他クーポンとの併用不可。","store":"全店","start":"2019-10-01","deadline":"2019-12-31","status":true},{"id":2,"code":"0002","benefit":"お会計を10%オフ!","explanation":"他クーポンとの併用不可","store":"有楽町店","start":"2019-10-01","deadline":"2019-12-31","status":true},{"id":3,"code":"0003","benefit":"【ハロウィン限定】仮装して来店すると30%オフ","explanation":"全身の50%以上を仮装されているお客様限定(判断はスタッフの感覚とさせて頂きます)。他クーポンとの併用不可","store":"神田店","start":"2019-10-31","deadline":"2019-10-31","status":true},{"id":4,"code":"0004","benefit":"【9月限定】お月見団子サービス","explanation":"ご希望のお客様に月見団子をプレゼント! 他クーポンとの併用可能です!","store":"全店","start":"2019-09-01","deadline":"2019-09-30","status":true},{"id":5,"code":"0005","benefit":"【雨の日限定】お会計から15%オフ","explanation":"クーポンが配信された時だけ利用可能です。他クーポンとの併用不可","store":"全店","start":"2019-10-01","deadline":"2019-12-31","status":false},{"id":6,"code":"0006","benefit":"【日曜日限定】乾杯テキーラサービス","explanation":"テキーラを人数分サービスします。他クーポンとの併用可。","store":"神田店","start":"2019-11-03","deadline":"2019-12-01","status":true}]


POSTを試します。

curl -X POST http://127.0.0.1:8000/api/coupons/ -d "code=0007" -d "benefit=お会計から19%引き" -d "explanation=12月29日~12月31日限定。 " -d "store=神田店" -d "start=2019-12-29" -d "deadline=2019-12-31" -d "status=true"

上手くいくとPOSTしたデータがjsonで返って来ます。


{"id":7,"code":"0007","benefit":"お会計から19%引き","explanation":"12月29日~12月31日限定。","store":"神田店","start":"2019-12-29","deadline":"2019-12-31","status":true}


PUTを試します。
PUTの時はHTTPメソッドを指定する必要があります。それより重要なのは、URLにテーブルのプライマリキーを指定する必要があります。couponモデルでは”id”がプライマリキーになっているので、URLの末尾に上書きしたいデータのid(ここでは 7 )を指定します。

curl -X PUT http://127.0.0.1:8000/api/coupons/7/ -d "code=0007" -d "benefit=お会計から19%引き" -d "explanation=12月29日~12月31日限定。他のクーポンとの併用不可 " -d "store=神田店" -d "start=2019-12-29" -d "deadline=2019-12-31" -d "status=true"

上手くいくと上書きされたデータがjsonで返って来ます。


{"id":7,"code":"0007","benefit":"お会計から19%引き","explanation":"12月29日~12月31日限定。他のクーポンとの併用不可","store":"神田店","start":"2019-12-29","deadline":"2019-12-31","status":true}


DELETEを試します。
先ほどPOSTしたリクエストをもう一度実行し、id=8のクーポンを作ります。GETリクエストかDjangoサーバのコンソールでid=8のクーポンが追加されたことを確認してください。

次に、下記のリクエストでid=8のクーポンを削除します。PUTと同様に削除対象のクーポンのプライマリキーの指定が必要です。

curl -X DELETE http://127.0.0.1:8000/api/coupons/8/

上手くいくと何も返って来ません。
GETリクエストかDjangoサーバのコンソールでid=8のクーポンが削除されたことを確認してください。

ここまでで基本的なRest Framework が出来ました。ここから 条件に合うクーポンのみをGETするためのフィルタや認証機能を追加していきます。