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

このエントリーをはてなブックマークに追加