Django REST FrameworkでAPIを実装

[未来電子テクノロジー](https://www.miraidenshi-tech.jp/intern-content/program/)でインターンをしている[箕牧和廣](https://qiita.com/mimaki_kazuhiro)です。
今回はDjango REST Frameworkについて書いていこうと思います。

プログラミング初心者であるため、内容に誤りがあるかもしれません。
もし、誤りがあれば修正するのでどんどん指摘してください。

##Django REST Frameworkとは
DjangoでWebAPIを開発することをサポートしてくれるライブラリ。RESTfulなAPIの開発をライブラリの標準としてサポートしている。

##早速インストール

#####必要なライブラリをインストール

```sh:terminal
$ pip install django
$ pip install djangorestframework
$ pip install django-filter 
```

#####プロジェクト・アプリの作成

```sh:terminal
# django_rest_framework_testという名前でプロジェクトを作ってみます
$ django-admin startproject django_rest_framework_test
$ cd django_rest_framework_test/

$ python manage.py startapp blog

# ディレクトリ構造確認
.
└── django_rest_framework_test
    ├── blog
    │   ├── __init__.py
    │   ├── admin.py
    │   ├── apps.py
    │   ├── migrations
    │   ├── models.py
    │   ├── tests.py
    │   └── views.py
    ├── django_rest_framework_test
    │   ├── __init__.py
    │   ├── settings.py
    │   ├── urls.py
    │   └── wsgi.py
    └── manage.py
```

#####モデルの定義

```sh:blog/models.py
from django.db import models


class User(models.Model):
    name = models.CharField(max_length=32)
    mail = models.EmailField()


class Entry(models.Model):
    STATUS_DRAFT = "draft"
    STATUS_PUBLIC = "public"
    STATUS_SET = (
            (STATUS_DRAFT, "下書き"),
            (STATUS_PUBLIC, "公開中"),
    )
    title = models.CharField(max_length=128)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    status = models.CharField(choices=STATUS_SET, default=STATUS_DRAFT, max_length=8)
    author = models.ForeignKey(User, related_name='entries', on_delete=models.CASCADE)
```

名前とメールアドレスだけを持った`User`と、ブログの記事を表す`Entry`を定義。`Entry`からブログの書いた人の情報としてForeignKeyで`User`を参照。

#####
データベース構築

```sh:django_rest_framework_test/settings.py
…
# blogを追記
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',
]
…
```


```sh:terminal
# migrationファイルを作る
$ python manage.py makemigrations
# migrationファイルを元にDBに反映する
$ python manage.py migrate
```

自分で定義したモデルを追加。

```sh:blog/admin.py
from django.contrib import admin

from .models import User, Entry


@admin.register(User)
class UserAdmin(admin.ModelAdmin):
    pass

@admin.register(Entry)
class Entry(admin.ModelAdmin):
    pass
```

#####動作確認

```sh:terminal
# admin用のユーザー作成
$ python manage.py createsuperuser
    Username (leave blank to use 'kimihiro_n'): dev
    Email address:
    Password:
    Password (again):

# 開発サーバーを起動
$ python manage.py runserver
```

[http://localhost:8000/admin](http://localhost:8000/admin)でDjangoの管理画面に入れる。
ここまでで通常のDjangoの流れは終了。

##Djagno REST Frameworkを組み込む

#####REST Frameworkの読み込み

```sh:django_rest_framework_test/settings.py
INSTALLED_APPS = (
    ...
    'blog',
    'rest_framework',
)
```


#####Serializerの定義

```sh:blog/serializer.py
# coding: utf-8

from rest_framework import serializers

from .models import User, Entry


class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model = User
        fields = ('name', 'mail')


class EntrySerializer(serializers.ModelSerializer):
    class Meta:
        model = Entry
        fields = ('title', 'body', 'created_at', 'status', 'author')
```

serializers.ModelSerializerを継承してModelに対応するSerializerを作成。 fieldsに与えるのはAPIとして出力したいフィールド名のタプル。filedsに列挙したものはデフォルトの実装でシリアライズして出力される。

#####ViewSetの定義
```sh:blog/views.py
# coding: utf-8

import django_filters
from rest_framework import viewsets, filters

from .models import User, Entry
from .serializer import UserSerializer, EntrySerializer


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


class EntryViewSet(viewsets.ModelViewSet):
    queryset = Entry.objects.all()
    serializer_class = EntrySerializer
```
querysetにDjangoのModelのクエリセットを、serializer_classに先ほど定義したSerializerを指定。querysetはあらかじめフィルタリングすることも可能。

#####URL pattern定義
```sh:django_rest_framework_test/urls.py
# coding: utf-8

from django.conf.urls import url, include
from django.contrib import admin

from blog.urls import router as blog_router

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    # blog.urlsをincludeする
    url(r'^api/', include(blog_router.urls)),
]
```

```sh:blog/urls.py
# coding: utf-8

from rest_framework import routers
from .views import UserViewSet, EntryViewSet


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

最後にURL patternの定義。routerというものを使ってModel毎に登録。
上記のように設定した場合、/api/がREST APIへの入り口となっており、GET /api/users/ でUserの一覧、GET /api/entries/でEntryの一覧へとアクセス出来る。

#####API動作確認
これでREST APIが動かせる状態になった。

```sh:terminal
python manage.py runserver
```
でサーバーを起動させ、http://localhost:8000/api/にアクセス。