[DRF]apiの作成（post）

# Django-rest-framework勉強中です
[前回](https://qiita.com/aja_min/items/ca360960a6f6c45cddcc)、[前々回](https://qiita.com/aja_min/items/eea4d49928a55946afbe)の記事のpostバージョンです。

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

``` python: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|２組２番|
|0004|2|1|hogehoge|地区2|２組１番|
|0003|1|3|山田３番|地区4|１組３番|
|0002|1|2|山田花子|地区2|１組２番|
|0001|1|1|山田太郎|地区1|１組１番|

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

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

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

### serializer

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

``` python: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文が流れるという感じです。


``` python: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に入れることとしています。

``` python: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())
]
```

### いざ実行

``` python: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を以下のように変えてみました。

``` python: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
```

### 実行

``` python: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?）　をして基礎編は終了したいと思います。