[DRF]apiの作成(post)


Django-rest-framework勉強中です

前回前々回の記事のpostバージョンです。

準備

前回と同じですが、一応Model構成載せておきます。

models.py
from django.db import models

DISTRICT_CATEGORIES = [
    (1, "地区1"),
    (2, "地区2"),
    (3, "地区3"),
    (4, "地区4"),
]


class Student(models.Model):
    """生徒情報"""

    # 生徒ID
    student_id = models.CharField(max_length=4, primary_key=True)

    # クラス
    class_no = models.CharField(max_length=1)

    # 出席番号
    attendance_no = models.IntegerField()

    # 名前
    name = models.CharField(max_length=20)

    # 地区番号
    district_no = models.CharField(max_length=1, choices=DISTRICT_CATEGORIES)

    # フリーコメント
    comment = models.CharField(max_length=200,blank=True)

    class Meta:
        constraints = [
            models.UniqueConstraint(
                fields=["class_no", "attendance_no"],
                name="class_attendance_unique"
            ),
        ]
STUDENT ID CLASS NO ATTENDANCE NO NAME DISTRICT NO COMMENT
0005 2 2 fugafuga 地区1 2組2番
0004 2 1 hogehoge 地区2 2組1番
0003 1 3 山田3番 地区4 1組3番
0002 1 2 山田花子 地区2 1組2番
0001 1 1 山田太郎 地区1 1組1番

post

今回はpostです。リソースの登録をする役割のあるメソッドですが、実際の運用ではもうちょっといろいろなことをやらせるAPIを準備しなくてはならないのかなと思います。

一旦はリソースの登録をするAPIを作成してみます。

レコードの新規作成

restの原則では、postはリソースの作成(CRUDで言うC)を行い、冪等性は保証しません。まぁ、今回はあまり気にせずDRFの書き方みたいな感じで軽く書いてみようかなと思います。

serializer

まぁ、getのときと一緒です。
ModelSerializerを継承して、studentモデルの全カラムを作成しにいきます。

serializers.py
from rest_framework import serializers
from ..models import Student


class StudentSerializer(serializers.ModelSerializer):
    """Studentクラスのシリアライザ"""

    class Meta:
        model = Student
        fields = '__all__'

view

簡単に書いています。実際の業務だともうちょい複雑なコードになるかもです。
リクエストをデシリアライズした後、serializer.is_valid()というメソッドを呼んでいます。一度バリデーションを通さないと次のserializer.save()が呼べないことになっています。validationの内容は、デフォルトではModelSerializerであればModelの定義に即しているかのみのチェックです。
例えば生徒情報IDというフィールドでは、

student_id = models.CharField(max_length=4, primary_key=True)

このようにModelを定義しているので、max_lengthが4文字かどうかなどのチェックがかかるということがわかります。

そしてその後の.save()で、SQLのcreate文が流れるという感じです。

views.py
from rest_framework.response import Response
from rest_framework.views import APIView

from ..serializers.student_create import StudentSerializer


class StudentCreateAPIView(APIView):
    """
    生徒情報新規登録API
    """

    def post(self, request, *args, **kwargs):

        # リクエストをシリアライズ
        serializer = StudentSerializer(data=request.data)

        # リクエストのバリデーション
        serializer.is_valid(raise_exception=True)

        # リソースの新規作成(create)
        serializer.save()

        # レスポンス
        return Response({'result':True})

url

pkはパスパラメータでなくbodyに入れることとしています。

urls.py
from django.urls import path
from . import views

urlpatterns = [
    # path('<pk>/retrieve/', views.StudentRetrieveAPIView.as_view())
    # path('<class_no>/<int:attendance_no>/retrieve/', views.StudentRetrieveAPIView.as_view()),
    path('create/', views.StudentCreateAPIView.as_view())
]

いざ実行

apitest.py
import requests

student_id='0006'
class_no='1'
attendance_no=4
name="黒崎一護"
district_no='4'
comment='尸魂界'
body = {
    'student_id':student_id,
    'class_no':class_no,
    'attendance_no':attendance_no,
    'name':name,
    'district_no':district_no,
    'comment':comment,
}
response = requests.post('http://127.0.0.1:8000/student/create/', body)
print(response.text)    # レスポンスのHTMLを文字列で取得

一意制約に気をつけて実行したところ、viewで定義したとおりresult:trueが返ってきました。
そして以下のレコードができていました!!

STUDENT ID CLASS NO ATTENDANCE NO NAME DISTRICT NO COMMENT
0006 1 4 黒崎一護 地区4 尸魂界

ちなみに・・・

validationに引っかかると、以下のようにエラーで教えてくれます。
student_id='00007'でリクエストすると、以下エラーとなります。

rest_framework.exceptions.ValidationError: {'student_id': [ErrorDetail(string='この項目が4文字より長くならないようにしてください。', code='max_length')]}

汎用APIViewも使ってみたい

views.pyを以下のように変えてみました。

views.py
from rest_framework import generics

from ..models import Student
from ..serializers import StudentSerializer


class StudentCreateAPIView(generics.CreateAPIView):
    queryset = Student.objects.all()
    serializer_class = StudentSerializer

実行

apitest.py
import requests

student_id='0007'
class_no='1'
attendance_no=5
name="朽木ルキア"
district_no='4'
comment='袖白雪'
body = {
    'student_id':student_id,
    'class_no':class_no,
    'attendance_no':attendance_no,
    'name':name,
    'district_no':district_no,
    'comment':comment,
}
# response = requests.get('http://127.0.0.1:8000/student/{}/retrieve/'.format(id))
response = requests.post('http://127.0.0.1:8000/student/create/', body)
print(response.text)    # レスポンスのHTMLを文字列で取得

できちゃった・・・。

STUDENT ID CLASS NO ATTENDANCE NO NAME DISTRICT NO COMMENT
0007 1 5 朽木ルキア 地区4 袖白雪

本当にコード量が少ないですね・・・。

ひとまず、postリクエストでリソースの新規作成ができました。
次はリソースの一部更新(patch? put?) をして基礎編は終了したいと思います。