Django2系とdjango rest frameworkでviewクラスの作成して、apiを実装する
前回、モデルのSerialize・Deserializeを行ったので、今回は、Viewクラスを作成して、
HTTP Requestの内容に応じて、データベースの中身をjson形式で返却する
いわゆるapiの作り方をに入りたいと思います。
MVCとMVPパターン
自分はserverサイドの人間ではないので、詳しい知識はありませんが、
apiを作る場合有名な構築パターンとして、mvc(Model View Controller)とmvp(Model View Presentation)があります。
今回は、Viewという名前のクラスにApiの処理を書いていますが、これはmvc・mvpに則ったDjangoのフレームワークによるものです。
Viewはその名の通りの意味で、今回のようにapiでなければ、HTMLを出力するクラスになります。
Viewクラス書く
前置きが終わったところで、Viewクラスを書いていきます。
resutful01/flowers/views.py
に書き込んでいきます。
作成するapiの内容ですが、
花のリストを返却する・生成するflower_listと
花の詳細を返却・更新・削除するflower_detail apiを作成します。
まず、JSON形式でレスポンスを返すために、HttpResponseクラスを継承したJSONResponseクラスを定義します。
class JSONResponse(HttpResponse):
def __init__(self, data, **kwargs):
content = JSONRenderer().render(data)
kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs)
次に花のリストを返却する・生成するflower_list apiを作ります。
まず、flowerテーブルのすべてのデータを返す、GET methodの定義をします。
@csrf_exempt
def flower_list(request):
if request.method == 'GET':
@csrf_exempt
まず、csrf対策をしないまま、POSTリクエストなどで、DBのカラムを生成しようとすると、
CSRF cookie not set.
と表示されて怒られるので、@csrf_exemptをつけて、対象のapiに関して免除するようにします。
セキュリティに関しては、後に対策する予定です。
django.http.HttpRequest
flower_list関数の引数requestは、django.http.HttpRequestオブジェクトになります。
HttpRequestオブジェクトのmethodをチェックして、処理を分岐させます。
apiの実装に戻りますが、まず、FlowerオブジェクトからDbの全てのオブジェクトを取得します。
そして、FlowerSerializerクラスを使用してJSON形式に変換できるようにします。
この時引数にmany=Trueとすることで、複数のオブジェクトを対象にすることができます。
flowers = Flower.objects.all()
flowers_serializer = FlowerSerializer(flowers, many=True)
return JSONResponse(flowers_serializer.data)
flower_list POST METHOD apiの実装
続いて、新たにFlowerオブジェクトを生成し、
flowerテーブルに追加するPOST METHODによるflower_list apiを見ていきます。
一般に新しいデータをtableに挿入する場合POST METHODとしてapiを作るようです。
こちらも前回作成したFlowerSerializerを使用して、送られたJSON形式のデータを
Flowerオブジェクトに変換し、flowerテーブルに保存しています。
データがJson形式として正しいかをis_valid関数を使ってチェックし、
正しい場合は、save関数を呼んでDBに保存し201を、そうでない場合は400を返します(201は成功など、番号は一般のルールとして定義されています)。
elif request.method == 'POST':
flower_data = JSONParser().parse(request)
flower_serializer = FlowerSerializer(data=flower_data)
if flower_serializer.is_valid():
flower_serializer.save()
return JSONResponse(flower_serializer.data,
status=status.HTTP_201_CREATED)
return JSONResponse(flower_serializer.errors,
status=status.HTTP_400_BAD_REQUEST)
flower_detail apiの実装
続いて、Flowerオブジェクトの詳細を返すflower_detail apiです。
flower_detail apiは特定の番号のFlower objectを返すので、まず主キーとなるpkを受けって、get関数を使ってflowerテーブルから対象を抜き出し、
対象が存在するかチェックします。
@csrf_exempt
def flower_detail(request, pk):
try:
flower = Flower.objects.get(pk=pk)
except Flower.DoesNotExist:
return HttpResponse(status=status.HTTP_404_NOT_FOUND)
flower_detail GET METHOD apiの実装
特定のFlower Objectを抜き出せたので、続いて、GET METHODを実装します。
pkに合致したFlower Objectをjsonで返すのみです。
if request.method == 'GET':
flower_serializer = FlowerSerializer(flower)
return JSONResponse(flower_serializer.data)
flower_detail PUT METHOD apiの実装
続いて、PUT methodです。
PUT methodはdataを更新する際に使われます。
elif request.method == 'PUT':
flower_data = JSONParser().parse(request)
flower_serializer = FlowerSerializer(flower, data=flower_data)
if flower_serializer.is_valid():
flower_serializer.save()
return JSONResponse(flower_serializer.data)
return JSONResponse(flower_serializer.errors, \
status=status.HTTP_400_BAD_REQUEST)
flower_detail DELETE METHOD apiの実装
最後にDELETE methodです。
その名の通り、DBから対象を消去する場合、対象をDELETE METHODとして定義します。
データの消去に成功したら、中身がないことを示す204を返します。
elif request.method == 'DELETE':
flower.delete()
return HttpResponse(status=status.HTTP_204_NO_CONTENT)
最後にviews.pyの全体のコードです。
from django.shortcuts import render
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt
from rest_framework.renderers import JSONRenderer
from rest_framework.parsers import JSONParser
from rest_framework import status
from flowers.models import Flower
from flowers.serializers import FlowerSerializer
class JSONResponse(HttpResponse):
def __init__(self, data, **kwargs):
content = JSONRenderer().render(data)
kwargs['content_type'] = 'application/json'
super(JSONResponse, self).__init__(content, **kwargs)
@csrf_exempt
def flower_list(request):
if request.method == 'GET':
flowers = Flower.objects.all()
flowers_serializer = FlowerSerializer(flowers, many=True)
return JSONResponse(flowers_serializer.data)
elif request.method == 'POST':
flower_data = JSONParser().parse(request)
flower_serializer = FlowerSerializer(data=flower_data)
if flower_serializer.is_valid():
flower_serializer.save()
return JSONResponse(flower_serializer.data, \
status=status.HTTP_201_CREATED)
return JSONResponse(flower_serializer.errors, \
status=status.HTTP_400_BAD_REQUEST)
@csrf_exempt
def flower_detail(request, pk):
try:
flower = Flower.objects.get(pk=pk)
except Flower.DoesNotExist:
return HttpResponse(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
flower_serializer = FlowerSerializer(flower)
return JSONResponse(flower_serializer.data)
elif request.method == 'PUT':
flower_data = JSONParser().parse(request)
flower_serializer = FlowerSerializer(flower, data=flower_data)
if flower_serializer.is_valid():
flower_serializer.save()
return JSONResponse(flower_serializer.data)
return JSONResponse(flower_serializer.errors, \
status=status.HTTP_400_BAD_REQUEST)
elif request.method == 'DELETE':
flower.delete()
return HttpResponse(status=status.HTTP_204_NO_CONTENT)
urls.pyを定義して、ルーティングを行う
apiを受け取り・レスポンスを返す準備ができたので、HttpRequestが正しく受け取れるように
ルーティングの設定を行います。
ルーティングとは、定義した~/flower_listなどのurlを受け取れるようにすること・不正なurlを弾く作業です。
では、resutful01/flowers/urls.pyを新たに生成し、
以下のように記述します。
from django.conf.urls import url
from flowers import views
urlpatterns = [
url(r'^flowers/$', views.flower_list),
url(r'^flowers/(?P[0-9]+)$', views.flower_detail),
]
まず、urlpatternsを定義してその中で、django.conf.urls.url関数(will be deprecated)を呼び出します。
引数に、有効となるurlを入れることにより、ルーティングを行うことができます。
続いて、restful01/restful01/urls.pyを
以下のように書き込みます。
from django.conf.urls import url, include
urlpatterns = [
url(r'^', include('flowers.urls')),
]
local serverを立ち上げる
では、local serverを立ち上げて、作成したapiを試してみたいと思います。
manage.pyを実行するために、
restful01フォルダ内に移動して、以下のコマンドを叩きます。
python manage.py runserver
curlを使って、HTTP requestを行い、apiの確認をする
local serverを立ち上げたら、curlコマンドでHTTP requestを出して、
apiの挙動を確認してみます。
まずは、flower_list apiを叩いて、flower tableのリスト一覧を取得してみます。
curl -X GET localhost:8000/flowers/
レスポンスは以下のようになります。
[{"pk":1,"name":"Lily","description":"Lily","release_date":"2018-05-19T07:50:51.031024Z","flower_category":"Lilium","was_included_in_home":false},{"pk":2,"name":"sakura","description":"sakura","release_date":"2018-05-19T10:20:00.111111Z","flower_category":"Rosaceae","was_included_in_home":false}]
dbのデータが反映され、json形式でResponseが返っていることが確認できます。
flower_list apiのPOST METHOD
続いて、新しくflower tableにカラムを追加するPOST METHODのapiを叩いてみます。
curl -iX POST -H "Content-Type: application/json" -d '{"name":"Cherry blossom", "description":"Cherry blossom",
"flower_category":"Rosaceae", "was_included_in_home": "false",
"release_date": "2018-05-10T01:01:00.555555Z"}' localhost:8000/flowers/
Responseは以下のようになり201が返却されているのが確認できます。
HTTP/1.1 201 Created
Date: Sun, 27 May 2018 09:18:00 GMT
Server: WSGIServer/0.2 CPython/3.6.4
Content-Type: application/json
X-Frame-Options: SAMEORIGIN
Content-Length: 166
{"pk":3,"name":"Cherry blossom","description":"Cherry blossom","release_date":"2018-05-10T01:01:00.555555Z","flower_category":"Rosaceae","was_included_in_home":false}(MyRestfulApi) MacBook-Pro:flowers shunichiro$
flower_detail apiのGET METHOD
続いて、flower_detail apiのGET METHODを叩きます。
主キーに応じた特定のflowerを返すので、index番号をリクエストと一緒に入れます。
curl -iX GET localhost:8000/flowers/2
レスポンスは以下になり、想定通りpk=2のFlower ObjectがJSON形式で返却されています。
HTTP/1.1 200 OK
Date: Sun, 27 May 2018 08:45:57 GMT
Server: WSGIServer/0.2 CPython/3.6.4
Content-Type: application/json
X-Frame-Options: SAMEORIGIN
Content-Length: 150
{"pk":2,"name":"sakura","description":"sakura","release_date":"2018-05-19T10:20:00.111111Z","flower_category":"Rosaceae","was_included_in_home":false}
対象となるFlower Objectが存在しない場合、status.HTTP_404_NOT_FOUNDが返るようにプログラムを組んだので、
その場合のResponseもチェックしましょう。
curl -iX GET localhost:8000/flowers/100
Responseは以下のようになり、想定通り404が返却されています。
HTTP/1.1 404 Not Found
Date: Sun, 27 May 2018 08:52:39 GMT
Server: WSGIServer/0.2 CPython/3.6.4
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 0
flower_detail apiのPOST METHOD
続いて、flower tableを更新するPUT METHODのapiを叩いてみます。
curl -iX PUT -H "Content-Type: application/json" -d '{"name":"Cherry blossom", "description":"Cherry blossom is sakura", "flower_category":"Rosaceae", "was_included_in_home": "false", "release_date": "2017-10-08T01:01:00.776594Z"}' localhost:8000/flowers/3
レスポンスは以下のようになり、pk = 3のデータが更新されていることが確認できます。
HTTP/1.1 200 OK
Date: Sun, 27 May 2018 09:23:26 GMT
Server: WSGIServer/0.2 CPython/3.6.4
Content-Type: application/json
X-Frame-Options: SAMEORIGIN
Content-Length: 176
{"pk":3,"name":"Cherry blossom","description":"Cherry blossom is sakura","release_date":"2017-10-08T01:01:00.776594Z","flower_category":"Rosaceae","was_included_in_home":false}(MyRestfulApi) MacBook-Pro:flowers shunichiro$
flower_detail apiのDELETE METHOD
最後に、flower_detail DELETE METHOD apiを叩いてみます。
先ほどcreateしたpk:3を消してみましょう。
curl -iX DELETE localhost:8000/flowers/3
レスポンスは以下のようになり、pk = 3のデータが消えていることが確認できます。
HTTP/1.1 204 No Content
Date: Mon, 28 May 2018 02:41:55 GMT
Server: WSGIServer/0.2 CPython/3.6.4
Content-Type: text/html; charset=utf-8
X-Frame-Options: SAMEORIGIN
Content-Length: 0
認証のところは省いていますが、よく使われるapiの実装を行い、挙動を確認しました。
こちらを参考に、知識をまとめています。
初版:2018/5/28