脱・とりあえず動く[DRF編]

# 課題
「業務で使っているけど、よく知らない。けどとりあえず動いている」ということが非常に多いので、調べたこと、試したことをまとめていこうと思います。
今回はDjango REST framework編です。

# そもそもRESTとは
参考：https://qiita.com/masato44gm/items/dffb8281536ad321fb08
Representation State Transferの略。

特徴としては

- ステートレス（同じリクエストに対して同じ結果を返す）
- リクエストメソッドの種類とCRUDが紐づいている(GET→READ、POST→CREATE、PUT→UPDATE、DELETE→DELETE)
- URLとメソッドの種類で、リソースに対する操作を表現する
    - メソッド：GET  URL：/users    → ユーザー全取得
    - メソッド：GET  URL：/users/1  → ユーザー1を取得　etc...


他にも色々あるけれども、このルールに則っているAPIを「RESTFulなAPI」と呼びます。
一番の特徴は「メソッドとURLでリソースを操作する」ことだと思います。

# メリット・デメリット
参考

- http://kageura.hatenadiary.jp/entry/2018/01/11/なぜポストREST_APIが求められるのか？_REST_APIがカバーでき
- https://qiita.com/mserizawa/items/b833e407d89abd21ee72

メリットとしては、

- URL、APIのルールがわかりやすい
    - 開発側も一貫したルールに基づいて開発しやすい
    - APIを使用する側も使い方がわかりやすい

デメリットとしては

- 複雑な処理の実装が煩雑
    - どうしてもRESTの考え方に合わない部分も出てくる

あくまでRESTは設計原則なので、原則に合わない部分をどうするか、という点で問題でしょうか。

# DRFについて
[Django REST Framework](http://www.django-rest-framework.org)(以下DRF)は、Django上でRESTFulなAPIを作成するためのフレームワークです。

ざっくりとした使用法としては、

- Modelを定義
    - Model：DBに登録するデータクラス。
- ModelのSerializerを定義
    - Serializer：実際のデータとModelの橋渡しをするクラス。Model ⇆ Serializer ⇆ DBやデータ　みたいなイメージ。
- Viewを定義
    - View：リクエストに対してどのようなレスポンスを返すかを定義したもの。DRFではAPIViewやViewSetというクラスにメソッド別の動作を定義する。
- URLを設定
    - 指定されたURLとViewを結びつける。

といった流れになります。DRF側で用意されているものを利用すれば、シンプルなAPIなら手軽に作成することができます。

# 実験
簡単なModelを定義して、CRUD処理を確認します

プロジェクト作成
`django-admin startproject sample`
アプリ作成
`python manage.py startapp drf_sample`

設定編集

```setting.py
# 追加分のみ
INSTALLED_APP = [
    'drf_sample',
    'rest_framework'
]

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        # テスト用に認証なしで操作できるように設定
        'rest_framework.permissions.AllowAny'
    ]
}
```

Modelの定義
djangoのデフォルトUserクラスはありますが、今回は実験のため、
first_name,last_name,emailというフィールドを持つUserModelを定義します。

```drf_sample.models.py
from django.db import models

class User(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)
    email = models.EmailField()
```

Serializerの定義
drf_sample配下にserializers.pyを作成します

```drf_sample.serializers.py
from drf_sample.models import User
from rest_framework import serializers

class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = '__all__'
```

viewの追加

```drf_sample.views.py
from rest_framework import viewsets
from drf_sample.models import User
from drf_sample.serializers import UserSerializer

class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
```

URLの追加
drf_sample配下にurls.pyを作成します

```drf_sample.urls.py
from rest_framework import routers
from drf_sample.views import UserViewSet

router = routers.DefaultRouter()
router.register(r'users', UserViewSet)

urlpatterns = router.urls
```

Migrate
`python manage.py makemigrations drf_sample`
`python manage.py migrate`

これで完了です。サーバーを起動します。
`python manage.py runserver`

curlコマンドでCRUD処理を確認します。

GET
`curl http://localhost:8000/api/users/`
`[]`
usersで全ユーザーを取得しましたが、まだユーザーがないので空のリストが返ってきました。
では、ユーザーを登録しましょう。
POST
`curl -X POST http://localhost:8000/api/users/ -d "first_name=first&last_name=last&email=drf_sample@gmail.com"`
`{"id":1,"first_name":"first","last_name":"last","email":"drf_sample@gmail.com"}`
登録が成功するとそのユーザーのデータが返ってきます。

改めてGETします。
`curl http://localhost:8000/api/users/`
`[{"id":1,"first_name":"first","last_name":"last","email":"drf_sample@gmail.com"}]`
先ほど登録したユーザーがリストで返ってきます。
一件取得は
`curl http://localhost:8000/api/users/1/`
`{"id":1,"first_name":"first","last_name":"last","email":"drf_sample@gmail.com"}`

これでGET(READ)、POST(CREATE)が確認できました。
PUT
`curl -X PUT http://localhost:8000/api/users/1/ -d "first_name=change_first_name&last_name=last&email=drf_sample@gmail.com"`
`{"id":1,"first_name":"change_first_name","last_name":"last","email":"drf_sample@gmail.com"}`

DELETE
`curl -X DELETE http://localhost:8000/api/users/1/`
確認
`curl http://localhost:8000/api/users/`
`[]`

これでCRUD処理は全て確認できました。
参考：http://www.django-rest-framework.org

# もっと詳しく
前章のコードをもう少し詳しく見てみます。

### Model
ModelクラスはDBのテーブルに対応し、各カラムが各フィールドに対応します。
それぞれの属性にふさわしいフィールドでModelを作成することで、APIを自動生成してくれます。
データのチェックも行ってくれるので、例えばemailフィールドに有効でないメールアドレスを渡すとエラーになったりします。(https://docs.djangoproject.com/ja/2.1/ref/validators/#django.core.validators.EmailValidator)
フィールド一覧：https://docs.djangoproject.com/ja/2.1/ref/models/fields/#model-field-types


### Serializer
Serializerの役目は、データとModelの変換です。

- Modelのフィールドに対するserializerを定義する
- create(Modelインスタンスの作成),update(Modelインスタンスの更新)等のメソッド,data(ModelをPythonの標準の形にしたもの)等のプロパティが準備されている

などの特徴があります。
必要に応じて各メソッドをオーバーライドしたり、オプションを追加します。
参考：https://www.django-rest-framework.org/api-guide/serializers/

### ViewSet
クラスベースのViewです。URLで呼び出されるメソッドを定義します。
(viewは関数でも良いのですが、クラスベースだと再利用しやすいため推奨されているようです)
今回使用したModelViewSetには、デフォルトで以下のメソッドが用意されています。

- list(全件取得)
- retrive(一件取得)
- create(新規作成)
- update(更新)
- partial_update(部分更新)
- destroy(削除)

参考:https://www.django-rest-framework.org/api-guide/viewsets/

### Router
ViewSetで定義したメソッドと、リクエストメソッドとURLを対応付けます。
デフォルトだと以下の対応になります。

| リクエストメソッド |          URL      |呼び出されるViewSetのメソッド |
|:----------------:|:-----------------:|:------------------:|
| GET              |           /users/ |        list        |
| GET              |      /users/{id}/ |      retrieve      |
| POST             |           /users/ |      create        |
| PUT              |      /users/{id}/ |     update         |
| PATCH            |      /users/{id}/ | partial_update     |
| DELETE           |      /users/{id}/ |      destroy       |

新たなURLを追加したい場合は、ViewSetにメソッドを定義→actionデコレータでリクエストメソッドを指定してやれば、そのメソッド名でURLに追加されます。

参考:https://www.django-rest-framework.org/api-guide/routers/

# まとめ
DRFを使用することで、RESTfulなAPIを簡単に開発することができます。
思った以上に便利な機能や設定等があり、うまく利用できればコーディング量を大幅に削減できます。
ただ、より複雑な場合はどのように設計・開発すべきかのベストプラクティスはまだまだ見えていないので、経験を積んで行きたいですね。

全く別件なのですが、先日[現場で使えるDjangoの教科書](https://www.amazon.co.jp/現場で使える-Django-の教科書《基礎編》-横瀬-明仁/dp/4802094744)を購入しました。まだ読了はしていないのですが、何と無く使っていた部分が簡潔に説明されており、納得しながら読み進められています。おすすめです。