Django python rest frameworkでrelationalなModelのviewの対応をする
前回は、serialize・deserializeの対応を行なったので、今回はapiリクエストを受け付けるために、
viewクラスにリクエストのレスポンスを書いていきます。
viewクラスにapiの挙動を書き込む
newFlowers/views.pyにapiの挙動を書き込みましょう
from django.shortcuts import render
from rest_framework import generics
from rest_framework.response import Response
from rest_framework.reverse import reverse
from newFlowers.models import FlowerCategory
from newFlowers.models import Flower
from newFlowers.models import Farmer
from newFlowers.models import Competition
from newFlowers.serializers import FlowerCategorySerializer
from newFlowers.serializers import FlowerSerializer
from newFlowers.serializers import FarmerSerializer
from newFlowers.serializers import FarmerCompetitionSerializer
class FlowerCategoryList(generics.ListCreateAPIView):
queryset = FlowerCategory.objects.all()
serializer_class = FlowerCategorySerializer
name = 'flowercategory-list'
class FlowerCategoryDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = FlowerCategory.objects.all()
serializer_class = FlowerCategorySerializer
name = 'flowercategory-detail'
class FlowerList(generics.ListCreateAPIView):
queryset = Flower.objects.all()
serializer_class = FlowerSerializer
name = 'flower-list'
class FlowerDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Flower.objects.all()
serializer_class = FlowerSerializer
name = 'flower-detail'
class FarmerList(generics.ListCreateAPIView):
queryset = Farmer.objects.all()
serializer_class = FarmerSerializer
name = 'farmer-list'
class FarmerDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Farmer.objects.all()
serializer_class = FarmerSerializer
name = 'farmer-detail'
class CompetitionList(generics.ListCreateAPIView):
queryset = Competition.objects.all()
serializer_class = FarmerCompetitionSerializer
name = 'competition-list'
class CompetitionDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Competition.objects.all()
serializer_class = FarmerCompetitionSerializer
name = 'competition-detail'
apiのスコープ
apiのスコープは以下のようになります。
スコープ | view name | HTTP verbs |
flower categoriesの集合: /flower-categories/ | FlowerCategoryList | GET, POST, and OPTIONS |
flower category: /flower-category/{id} | FlowerCategoryDetail | GET, PUT, PATCH, DELETE, and OPTIONS |
flowerの集合: /flowers/ | FlowerList | GET, POST, and OPTIONS |
flower: /flower/{id} | FlowerDetail | GET, PUT, PATCH, DELETE, and OPTIONS |
farmerの集合: /farmers/ | FarmerList | GET, POST and OPTIONS |
Farmer: /Farmer/{id} | FarmerDetail | GET, PUT, PATCH, DELETE and OPTIONS |
competitionの集合: /competitions/ | CompetitionList | GET, POST and OPTIONS |
competition: /competition/{id} | CompetitionDetail | GET, PUT, PATCH, DELETE and OPTIONS |
apiのroutingの追加
先ほどのスコープに対応させるために、newFlowers/views.pyに
routingを追加します。
ApiRootクラスを先頭に以下のように追加します。
class ApiRoot(generics.GenericAPIView):
name = 'api-root'
def get(self, request, *args, **kwargs):
return Response({
'flower-categories': reverse(FlowerCategoryList.name, request=request),
'flowers': reverse(FlowerList.name, request=request),
'farmers': reverse(FarmerList.name, request=request),
'competitions': reverse(CompetitionList.name, request=request)
})
class ApiRootは処理を簡略化させるために、generics.GenericApiViewを継承しています。 また、get関数を定義し、viewとurlに対応したResponseオブジェクトを返しています。
reverse関数はurlを返してくれます。
詳しくはリンクを。
url.クラスを定義してurlを制限する
対象のurlを制限するために、restful01/newFlowers/urls.pyを作成し、以下のように書き込みます。
from django.conf.urls import url
from newFlowers import views
urlpatterns = [
url(r'^flower-categories/$',
views.FlowerCategoryList.as_view(),
name=views.FlowerCategoryList.name),
url(r'^flower-categories/(?P[0-9]+)$',
views.FlowerCategoryDetail.as_view(),
name=views.FlowerCategoryDetail.name),
url(r'^flowers/$',
views.FlowerList.as_view(),
name=views.FlowerList.name),
url(r'^flowers/(?P[0-9]+)$',
views.FlowerDetail.as_view(),
name=views.FlowerDetail.name),
url(r'^farmers/$',
views.FarmerList.as_view(),
name=views.FarmerList.name),
url(r'^farmers/(?P[0-9]+)$',
views.FarmerDetail.as_view(),
name=views.FarmerDetail.name),
url(r'^competitions/$',
views.CompetitionList.as_view(),
name=views.CompetitionList.name),
url(r'^competitions/(?P[0-9]+)$',
views.CompetitionDetail.as_view(),
name=views.CompetitionDetail.name),
url(r'^$',
views.ApiRoot.as_view(),
name=views.ApiRoot.name),
]
つづいて、restful01/urls.pyに以下のように書き込みます。
urlpatterns = [
url(r'^', include('newFlowers.urls')),
]
これで、viewの作成とrouting処理が完成しました。
apiを叩いて、挙動を確認する
一体多のapiの確認をしてみます。
まず、FlowerCategoryを追加します。
curl -iX POST -H "Content-Type: application/json" -d '{"name":"Liliaceae"}' localhost:8000/flower-categories/
続いて、花を2つ追加します。
curl -iX POST -H "Content-Type: application/json" -d '{"name":"newLily", "flower_category":"Liliaceae", "production_date": "2018-07-20T02:02:00.716312Z", "has_it_competed": "false"}' localhost:8000/flowers/
curl -iX POST -H "Content-Type: application/json" -d '{"name":"tinyLily", "flower_category":"Liliaceae", "production_date": "2018-08-20T02:02:00.716312Z", "has_it_competed": "false"}' localhost:8000/flowers/
ここで、カテゴリーにないflowerを追加してみましょう。
curl -iX POST -H "Content-Type: application/json" -d '{"name":"myRose", "flower_category":"Rosaceae", "production_date": "2018-10-23T02:03:00.716312Z", "has_it_competed": "false"}' localhost:8000/flowers/
外部キーがないので、追加できないはずです。
ということで、レスポンスは以下のようになります。
HTTP/1.1 400 Bad Request
Date: Sun, 08 Jul 2018 08:47:45 GMT
Server: WSGIServer/0.2 CPython/3.6.4
Content-Type: application/json
Vary: Accept, Cookie
Allow: GET, POST, HEAD, OPTIONS
X-Frame-Options: SAMEORIGIN
Content-Length: 65
{"flower_category":["Object with name=Rosaceae does not exist."]}
ApiでFarmerとCompetitionを追加する
続いて、FarmerとCompetitionを追加してみます。
まずは、Farmerを2つ作ります。
curl -iX POST -H "Content-Type: application/json" -d '{"name":"jiji", "gender":"M", "competitions_count": 0}' localhost:8000/farmers/
curl -iX POST -H "Content-Type: application/json" -d '{"name":"jiji1000", "gender":"M", "competitions_count": 0}' localhost:8000/farmers/
続いてcompetition2つを作ります。
curl -iX POST -H "Content-Type: application/json" -d '{"score":"60", "score_achievement_date":"2018-10-20T05:03:20.776594Z", "farmer":"jiji", "flower":"newLily"}' localhost:8000/competitions/
curl -iX POST -H "Content-Type: application/json" -d '{"score":"62", "score_achievement_date":"2018-10-21T06:02:23.776594Z", "farmer":"jiji1000", "flower":"tinyLily"}' localhost:8000/competitions/
テーブルの相関図
dbのtableをデータを挿入したので、tableの相関図を確認してみます。
mysql> select * from newFlowers_flowercategory;
+----+-----------+
| id | name |
+----+-----------+
| 1 | Liliaceae |
+----+-----------+
mysql> select * from newFlowers_flower;
+----+----------+----------------------------+-----------------+----------------------------+--------------------+
| id | name | production_date | has_it_competed | inserted_timestamp | flower_category_id |
+----+----------+----------------------------+-----------------+----------------------------+--------------------+
| 1 | newLily | 2018-07-20 02:02:00.716312 | 0 | 2018-07-08 08:30:04.701664 | 1 |
| 2 | tinyLily | 2018-08-20 02:02:00.716312 | 0 | 2018-07-08 08:37:58.067468 | 1 |
+----+----------+----------------------------+-----------------+----------------------------+--------------------+
mysql> select * from newFlowers_farmer;
+----+----------+--------+--------------------+----------------------------+
| id | name | gender | competitions_count | inserted_timestamp |
+----+----------+--------+--------------------+----------------------------+
| 1 | jiji | M | 0 | 2018-07-08 08:54:21.493960 |
| 2 | jiji1000 | M | 0 | 2018-07-15 03:41:18.947144 |
+----+----------+--------+--------------------+----------------------------+
mysql> select * from newFlowers_competition;
+----+-------+----------------------------+-----------+-----------+
| id | score | score_achievement_date | farmer_id | flower_id |
+----+-------+----------------------------+-----------+-----------+
| 1 | 60 | 2018-10-20 05:03:20.776594 | 1 | 1 |
| 2 | 62 | 2018-10-21 06:02:23.776594 | 2 | 2 |
+----+-------+----------------------------+-----------+-----------+
こんな感じで、relationalなdbができました。
こちらを参考に、知識をまとめています。
初版:2018/7/15