[DRF]apiの作成（get）part1

# django-rest-frameworkを勉強しています
まだ初心者なのですが、DRFの勉強中です。
[公式](https://www.django-rest-framework.org/)は英語だし、ググってもなかなか体系化された情報が出てこなくて苦労しています。
コードを読んでも「なんでこのコードだけでDB取得できるの！？」と思わされることが多く・・・。
勉強がてら、サンプルコードを書きながら意味のわからない用語や単語については解説していければと思います。勉強中なので変な書き方、もっと良い書き方等あるかと思いますが、ご容赦ください。（コメントなどで教えていただければ・・・。）

# 今回の記事では
DRFでgetのAPIを作成する際のコードを書いていきます。今回は単一オブジェクトのみの取得です。
getの中でも単一行取得だったり、複数行の取得だったりがあると思うので、両方のパターンを今後見ていけたらと思います。また、もしあれば「こんなパターンもあるよね」と横道にそれて行こうと思います。

# 準備
Djangoプロジェクトの作成や管理サイトについての説明は省きます。
まず、以下のような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"
            ),
        ]
```

せっかくなのでCharFieldだけじゃなくIntegerFieldだったり、Choiceの設定などもしています。また、クラス・出席番号での生徒の取得もしたいので、UniqueConstraint（クラス、出席番号のペアは一意）の成約を加えました。

また、とりあえず以下のデータを管理サイトから作成しておきました。

|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|１組１番

# get
では、このmodelに対して、一覧や単一行を取得するコードを書いてみましょう。

## pkから単一行取得
今回、modelでstudent_idをpkにしていますので、これを使えばデータの単一行での取得は可能です。

### serializer

#### serializerって？
一応自己の理解のために簡単な説明を入れておきますね。
オブジェクトのシリアライズ・デシリアライズをすることができます。シリアル（直列）化ですね。ネットワークでの通信が必要な場合、受け取ったデータをデシリアライズしてdjangoで扱えるようにしたり、シリアライズして送ったりするようです。ここも詳しく今後調べて行けるといいなーと思います。


#### serializerを作成します
取得をしたあと、レスポンスを作成するためのserializerを作成します。

``` python:serializers.py
from rest_framework import serializers
from .models import Student


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

    class Meta:
        model = Student
        fields = '__all__'

```

StudentModelに紐付いたシリアライザです。fieldはallとしています。

### view

#### viewって？
djangoのviewと同じです。
リクエストを受け取り、レスポンスを作成して返すところまでの処理をします。

#### viewを作成します
``` python:views.py
from django.shortcuts import get_object_or_404
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response

from .models import Student
from .serializers import StudentSerializer


class StudentRetrieveAPIView(APIView):

    def get(self, request, pk, *args, **kwargs):
        """Studentをpkで取得"""

        # モデルのオブジェクト取得
        instance = get_object_or_404(Student, pk=pk)

        # シリアライズ
        serializer = StudentSerializer(instance)

        # 返却
        return Response(serializer.data, status.HTTP_200_OK)

```

get_object_or_404はdjangoが用意してくれているものですね。get_objectで１件の取得を表している感じでしょうか。
**※もうちょっと便利なクラスを継承してコードの量減らしてもいいのですが、今回は勉強混じりということで、そちらは後ほど紹介します！**
ここで、`get`メソッドの引数に`pk`という項目が存在しますね。
この`pk`はurls.pyで指定が可能です。
以下のとおりです。

### urls.py
urls.pyで以下のように、呼び出されるURLとそれに応じて呼ばれるViewが対応つけられます。

プロジェクトディレクトリのurls.pyは以下の記述

```python:urls.py
from django.contrib import admin
from django.urls import path,include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('student/', include('students.urls'))
]
```
（adminについては気にしなくて大丈夫です）

studentsアプリの配下にはurls.pyを作成して以下の記述をします。

```python:students/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('<pk>/retrieve/', views.StudentRetrieveAPIView.as_view())
]
```

### 実行
ここまでで、serializer,view,urlsの準備ができたので実際にAPIを実行してみましょう。

実行ファイルはこちらです。

``` python:apitest.py
import requests

id='0001'
response = requests.get('http://127.0.0.1:8000/student/{}/retrieve'.format(id))
print(response.text)    # レスポンスのHTMLを文字列で取得
```

> {"student_id":"0001","class_no":"1","attendance_no":1,"name":"山田太郎","district_no":"1","comment":"１組１番"}


想定のものが返ってきました。よかったー。

## pk以外の複数の値から単一行取得
次はクラスと出席番号から、行を取得します。
変更するのはurlとviewの部分です。

### urls.py

URLでクラス、出席番号を指定してもらいます。

``` diff_python:urls.py
urlpatterns = [
-    path('<pk>/retrieve/', views.StudentRetrieveAPIView.as_view())
+    path('<class_no>/<int:attendance_no>/retrieve/', views.StudentRetrieveAPIView.as_view())
]
```

### views.py

getの引数にURLから受け取ったものを加え、get_objectの際のキーに指定します。

``` diff_python:views.py
class StudentRetrieveAPIView(APIView):
-    def get(self, request, pk, *args, **kwargs):
+    def get(self, request, class_no, attendance_no, *args, **kwargs):   
        """Studentをclass_no, attendance_noで取得"""

        # モデルのオブジェクト取得
-        instance = get_object_or_404(Student, pk=pk)
+        instance = get_object_or_404(Student, class_no=class_no, attendance_no=attendance_no)

        # シリアライズ
        serializer = StudentSerializer(instance)

        # 返却
        return Response(serializer.data, status.HTTP_200_OK)
```

### 実行

``` python:apitest.py
import requests

id='0001'
class_no='1'
attendance_no=2
# response = requests.get('http://127.0.0.1:8000/student/{}/retrieve'.format(id))
response = requests.get('http://127.0.0.1:8000/student/{}/{}/retrieve'.format(class_no, attendance_no))
print(response.text)
```

> {"student_id":"0002","class_no":"1","attendance_no":2,"name":"山田花子","district_no":"2","comment":"１組２番"}

できた！

とりあえず、getについてのAPIを作ってみました。
複数オブジェクトの取得とか、もっと便利なAPIViewを使って簡単にかけると思いますので、引き続き次回以降の記事で勉強していきます。