Django
python3
Pycharm
django-rest-framework

Django REST Framework の使い方メモ

学習履歴

■はじめに

Django REST framework を使って、REST API の作成方法を学んでみる。

■環境

Mac
Python3.7
pipenv
Django2.1
Pycharm

■Library API の構築

サンプルとして、Library API を作りつつ、Django REST Framework を学んで行こう。

ただ、Django REST framework だけでは Web API を構築することができないので、
まずは、Django のアプリケーションを作り、その後、 REST framework を追加する。

■ Djangoプロジェクトの作成

pipenv を使って、仮想環境を構築して、そこに Django をインストールする。

terminal
$ mkdir django_rest_framework
$ cd django_rest_framework/
$ mkdir code
$ cd code/
$ pwd
/Users/hoge/Desktop/django_rest_framework/code

# Django をインストール
$ pipenv install django==2.1
# 仮想環境に入る
$ pipenv shell
(code-V3vlQwAZ) bash-3.2$

# Django のプロジェクトを作成
# . は、必ず必要
(code-V3vlQwAZ) bash-3.2$ django-admin startproject library_project .
(code-V3vlQwAZ) bash-3.2$ tree
.
├── Pipfile
├── Pipfile.lock
├── library_project
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

# マイグレーションを実行
(code-V3vlQwAZ) bash-3.2$ python manage.py migrate

# ローカルサーバを起動
(code-V3vlQwAZ) bash-3.2$ python manage.py runserver

ここまで作業できたら、localhost:8000 にアクセスしてみる。

スクリーンショット 2018-09-23 11.46.49.png

■ アプリケーションの追加

Ctrl + c でローカルサーバーを止めて、アプリケーションを追加する。

terminal
# アプリケーションを追加
(code-V3vlQwAZ) bash-3.2$ python manage.py startapp books
(code-V3vlQwAZ) bash-3.2$ tree
.
├── Pipfile
├── Pipfile.lock
├── books
│   ├── __init__.py
│   ├── admin.py
│   ├── apps.py
│   ├── migrations
│   │   └── __init__.py
│   ├── models.py
│   ├── tests.py
│   └── views.py
├── db.sqlite3
├── library_project
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── manage.py

■ アプリケーションの設定

books アプリケーションを追加できたら、いくつか設定を行う必要がある。

最初に settings.py に books アプリケーションを追加する。

library_project/settings.py
INSTALLED_APPS = [
    # Local
    'books.apps.BooksConfig', # 追加

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

それからこの変更をデータベースに反映させるために migrate を行う。

terminal
(code-V3vlQwAZ) $ python manage.py migrate

books アプリには、view, url, そしてテンプレートファイルが必要になるので、この後作成する。

■ Modelの作成

Django では、データベースの元となるデータを Model という形で作成する。

books/models.py
from django.db import models


class Book(models.Model):
    title = models.CharField(max_length=250)
    subtitle = models.CharField(max_length=250)
    author = models.CharField(max_length=100)
    isbn = models.CharField(max_length=13)

    def __str__(self):
        return self.title

続いて、マイグレーションを行う。

terminal
(code-V3vlQwAZ) $ python manage.py makemigrations books
(code-V3vlQwAZ) $ python manage.py migrate

■ 管理者の作成

続いて、Django の管理者の作成を行う。

terminal
(code-V3vlQwAZ) $ python manage.py createsuperuser
Username (leave blank to use 'hono'): admin
Email address:  
Password: 
Password (again): 
This password is too short. It must contain at least 8 characters.
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

管理者を作成すると管理者用のページにアクセスできるようになるが、デフォルトでは books アプリが
表示されない。

表示するには、以下の設定を行う。

books/admin.py
from django.contrib import admin
from .models import Book

admin.site.register(Book)

設定が完了したら python manage.py runserver を実行して、localhost:8000/admin にアクセスしてみよう。

スクリーンショット 2018-09-23 16.45.19.png

スクリーンショット 2018-09-23 16.45.56.png

管理者用ページに Books アプリが登録されていることがわかる。

Books アプリに適当にデータを入れてみよう。

スクリーンショット 2018-09-23 16.47.49.png

SAVE ボタンを押下するとデータが登録される。

スクリーンショット 2018-09-23 16.48.44.png

管理者ページを作成し、データの登録までできるようになった。
続いて、Veiw を作成しよう。

■ View の作成

views.py にて、データベースの Model をどのように画面に表示するかコントロールすることができる。
ここでは、Django の組み込み関数の ListView を使ってみよう。

books/views.py
from django.views.generic import ListView
from .models import Book


class BookListView(ListView):
    model = Book
    template_name = 'book_list.html'

BookListView クラスでは、Book Model と book_list.html テンプレート(これから作成する)を使用する設定をしている。
return 文とか設定しなくても ListView を継承することで、データを受け渡しできるらしい。

あとは、url と テンプレートを作成する必要がある。

■ URLs の作成

プロジェクトレベルの urls.py に book アプリへ繋がるパスを記述する必要がある。

library_project/urls.py
from django.contrib import admin
from django.urls import path, include # 追加

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('books.urls')), # 追加
]

次に アプリレベルの urls.py を作成する。

terminal
(code-V3vlQwAZ) $ touch books/urls.py

ここに、book アプリの view へのパスを記述する。

books/urls.py
from django.urls import path

from .views import BookListView


urlpatterns = [
    path('', BookListView.as_view(), name='home')
]

最後にテンプレートを作成する。

■テンプレートの作成

テンプレートファイルは、book ディレクトリ配下に作成する。

(code-V3vlQwAZ) $ mkdir books/templates
(code-V3vlQwAZ) $ mkdir books/templates/books
(code-V3vlQwAZ) $ touch books/templates/books/book_list.html
(code-V3vlQwAZ) $ tree books
books
├── __init__.py
├── __pycache__
│   ├── __init__.cpython-37.pyc
│   ├── admin.cpython-37.pyc
│   ├── apps.cpython-37.pyc
│   └── models.cpython-37.pyc
├── admin.py
├── apps.py
├── migrations
│   ├── 0001_initial.py
│   ├── __init__.py
│   └── __pycache__
│       ├── 0001_initial.cpython-37.pyc
│       └── __init__.cpython-37.pyc
├── models.py
├── templates
│   └── books
│       └── book_list.html
├── tests.py
├── urls.py
└── views.py

book_list.html にて、 View から渡されたデータを「こう表示しますよ」みたいな設定をする。

books/templates/books/book_list.html
<h1>All books</h1>
{% for book in object_list %}
    <ul>
        <li>Title: {{ book.title }}</li>
        <li>Subtitle: {{ book.subtitle }}</li>
        <li>Author: {{ book.author }}</li>
        <li>ISBN: {{ book.isbn }}</li>
    </ul>
{% endfor %}

for タグは、Django の template language というやつで、他にも色々な種類がある。

ListView のオブジェクトである object_list には、Books モデルの全てのデータが格納されているので、for タグで
画面上に全て表示するようにしている。

ここまでできたら画面上に Model の中身を表示できるようになっているはずだ。

python manage.py runserver でローカルサーバを起動し、localhost:8000 にアクセスする。

スクリーンショット 2018-09-23 18.43.09.png

うまくいったので、いよいよ Django REST Framework を導入してみよう。

■ Django REST Framework の導入

Django REST Framework は、Django のサードパーティアプリとして追加する。

ローカルサーバが起動している場合は、Ctrl + c で一旦停止してから、以下のコマンドを実行する。

terminal
(code-V3vlQwAZ) $ pipenv install djangorestframework==3.8.2

コマンド実行後、このフレームワークをアプリとして、Django に登録する。

library_project/settings.py
INSTALLED_APPS = [
    # Local
    'books.apps.BooksConfig',

    # 3rd party
    'rest_framework', # 追加

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

次は、専用の api アプリを作成する。

terminal
(code-V3vlQwAZ) $ python manage.py startapp api

これも Django にアプリとして認識させる。

library_project/settings.py
INSTALLED_APPS = [
    # Local
    'books.apps.BooksConfig',
    'api.apps.ApiConfig', # 追加

    # 3rd party
    'rest_framework',

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

この api アプリは、専用のデータベースを持たないので、migration は不要である。

次に、url の設定を行う。(流れは、bookアプリの時と一緒)

library_project/urls.py
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('books.urls')),
    path('api/', include('api.urls')), # 追加
]
terminal
(code-V3vlQwAZ) $ touch api/urls.py
api/urls.py
from django.urls import path

from .views import BookAPIView


urlpatterns = [
    path('', BookAPIView.as_view()),
]

次は、view の設定をする。

api/views.py
from rest_framework import generics

from books.models import Book
from .serializers import BookSerializer


class BookAPIView(generics.ListAPIView):
    queryset = Book.objects.all()
    serializer_class = BookSerializer

ListAPIView を継承したクラスを作成するとBook Model に対して、読み取り専用のエンドポイントを
提供できるようになるらしい。

Serializer は、Django のモデルデータを JSON 形式に変換して処理できるようにするためのもので、これから作成する。

terminal
(code-V3vlQwAZ) $ touch api/serializers.py

■ 動作確認

設定は全て完了したので、動作確認をしてみよう。

terminal
(code-V3vlQwAZ) $ python manage.py runserver

別のターミナルを立ち上げて、以下のコマンドを実行する。

terminal
(code-V3vlQwAZ) $ curl http://127.0.0.1:8000/api/
[{"title":"Django for Beginners","subtitle":"Build websites with Python and Django","author":"Rarara O. Tomas","isbn":"234-156789767"}]

うまくデータを取得できているようだ。

http://localhost:8000/api/ にブラウザからアクセスしてみよう。

スクリーンショット 2018-09-23 19.16.16.png

Django REST Framework ではこのような画面をデフォルトで提供してくれる。

■Todo API の構築

さて、今度は、Todo リストの API を作ってみよう。

今度は、back-end 側を Django で、front-end 側を React で実装してみる。
*前回の pipenv の仮想環境からは、deactivate or exit で抜けておくこと

terminal
$ mkdir todo && cd todo/
$ mkdir backend && cd backend
$ pipenv install django==2.1
$ pipenv shell
(backend-zfnLEadP) bash-3.2$ django-admin startproject todo_project .
(backend-zfnLEadP) bash-3.2$ python manage.py startapp todos
(backend-zfnLEadP) bash-3.2$ python manage.py migrate
(backend-zfnLEadP) bash-3.2$ tree
.
├── Pipfile
├── Pipfile.lock
├── db.sqlite3
├── manage.py
├── todo_project
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── todos
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    └── views.py

Work ディレクトリの作成から Django のアプリケーションを作成するところまで一気に行った。

todo_project/settings.py
INSTALLED_APPS = [
    # Local
    'todos.apps.TodosConfig',

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

ローカルサーバを起動し、localhost:8000/ にアクセスして、Django が起動しているか確認しよう。

terminal
(backend-zfnLEadP) bash-3.2$ python manage.py runserver

スクリーンショット 2018-09-24 7.06.27.png

■ Model の作成

次は、Todo API の Model を作成する。

todos/models.py
from django.db import models


class Todo(models.Model):
    title = models.CharField(max_length=200)
    body = models.TextField()

    def __str__(self):
        return self.title

Model を作ったら Ctrl + c でローカルサーバを停止し、以下のコマンドを実行する。

terminal
(backend-zfnLEadP) bash-3.2$ python manage.py makemigrations todos
(backend-zfnLEadP) bash-3.2$ python manage.py migrate

■ 管理者ページの設定

管理者ページも設定しておこう。

todos/admin.py
from django.contrib import admin
from .models import Todo

admin.site.register(Todo)

管理者も作成する。

terminal
(backend-zfnLEadP) bash-3.2$ python manage.py createsuperuser
Username (leave blank to use 'hono'): admin
Email address: 
Password: 
Password (again): 
This password is too short. It must contain at least 8 characters.
This password is too common.
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

ローカルサーバを起動して、localhost:8000/admin にアクセスしてみる。

スクリーンショット 2018-09-24 7.16.59.png

管理者ページは、問題なく作成されているようだ。

Todo の "+ Add" を押下して、アイテムを 3 つ追加する。

スクリーンショット 2018-09-24 7.19.16.png

■ Django REST Framework の追加

Control + c で、ローカルサーバを停止し、Django REST Framework をインストールする。

terminal
(backend-zfnLEadP) bash-3.2$ pipenv install djangorestframework==3.8.2

Django REST Framework をインストールしたら、settings.py の設定を行う。

todo_project/settings.py
INSTALLED_APPS = [
    # Local
    'todos.apps.TodosConfig',

    # 3rd party
    'rest_framework',

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

# Setting permission
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ]
}

Django REST Framework への設定は、Django の settings.py ファイル内に記述するらしい。

上記以外にも[様々な設定]があるみたいだ。

■ ルーティングの設定

ユーザのアクセスポイントを設定する。

まずは、プロジェクトレベルの urls.py を設定する。

todo_project/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('todos.urls')),
]

次に、以下のコマンドで、アプリレベルの urls.py を作成する。

terminal
(backend-zfnLEadP) bash-3.2$ touch todos/urls.py

ここに、views へのパスを記述する。

todos/urls.py
from django.urls import path

from .views import ListTodo, DetailTodo

urlpatterns = [
    path('<int:pk>/', DetailTodo.as_view()),
    path('', ListTodo.as_view()),
]

ListTodo と DetailTodo は、この後、views.py に設定する。

ルーティングの設定は、以上だ。

■ シリアライズ化

Django 上のデータの実態は、Model だ。(この表現が正しいのかさておき)

Djang REST Framework 場で、データをやり取りする際は、JSON 形式等に変換してからやり取りする
必要があるらしいので、データのシリアライズ化が必要になる。

Model -> JSON

まずは、以下のファイルを作成する。

terminal
(backend-zfnLEadP) bash-3.2$ touch todos/serializers.py

作成したファイルに対して、以下の設定を行う。

todos/serializers.py
from rest_framework import serializers
from .models import Todo


class TodoSerializer(serializers.ModelSerializer):
    class Meta:
        model = Todo
        fields = ('id', 'title', 'body',)

■ views の設定

views.py では、テンプレートへどのような形でデータを送信するか定義できる。

todos/views.py
from rest_framework import generics
from .models import Todo
from .serializers import TodoSerializer


class ListTodo(generics.ListAPIView):
    queryset = Todo.objects.all()
    serializer_class = TodoSerializer


class DetailTodo(generics.RetrieveAPIView):
    queryset = Todo.objects.all()
    serializer_class = TodoSerializer

Viewの設定として、ListAPIView は、Todo アプリの全てのモデルデータを表示し、RetrieveAPIView は、
モデルデータの一つを表示する仕組みを提供するらしい。

■ 動作確認

ここまで設定できたら動作確認をしてみよう。

ローカルサーバを起動する。

terminal
(backend-zfnLEadP) bash-3.2$ python manage.py runserver

localhost:8000/api/ にアクセスして、データが表示できるか確認してみよう。

スクリーンショット 2018-09-24 18.52.51.png

ListAPIView のおかげで、Todo アプリの全てのアイテムが表示されている。

今度は、localhost:8000/api/1 にアクセスしてみよう。

スクリーンショット 2018-09-24 18.56.01.png

api/1 の 1 は、モデルデータを追加するとき Django が自動で生成してくれる primary key だ。
アプリ側の settings.py に <int:pk> と設定している 

■ CORS

Cross-Origin Resource Shareing(CORS)は、異なるドメイン間(localhost:3000 <----> localhost:8000>で動作する
アプリケーション同士がリソースを共有できるようにする仕組みらしい。

backend を Django で、frontend を React での実装を試みようとしているが、これらのアプリケーションがリソースを
共有できるようにしたい。(ここで言うリソースとは、Django の todo Model のこと)

CORS を実現するには、http 通信に CORS ヘッダーを含める必要があるらしい。

django-cors-headersを使用すると最小限のコーディングで、実装が行えるようだ。

(backend-zfnLEadP) bash-3.2$ pipenv install django-cors-headers==2.4.0

追加したら以下の 3 つのことを行う。

  1. INSTALLED_APPS に corsheaders を追加
  2. 指定の middleware を 2 つ追加
  3. CORS_ORIGIN_WHITELIST を作成

これらは、setting.py 場で設定する。

todo_project/settings.py
INSTALLED_APPS = [
    # Local
    'todos.apps.TodosConfig',

    # 3rd party
    'rest_framework',
    'corsheaders',# 追加

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',# 追加
    'django.middleware.common.CommonMiddleware',# 追加
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
# 追加
CORS_ORIGIN_WHITELIST = (
    'localhost:3000/'
)

localhost:3000 は、React のデフォルトのドメインなので上記のように設定している。

■ Reactの実装

backend 側の Django の実装は完了したので、frontend 側 の React の実装に入ろう。

React は、javascript のフレームワークの一つで、NodeJs 場で動く。

Django のローカルサーバを動作させている terminal とは別の terminal を立ち上げて、NodeJs を brew でインストールしよう。

terminal
$ brew install node
$ sudo chown -R $USER /usr/local # 必須ではない
$ brew link node # 必須ではない
$ brew link --overwrite node # 必須ではない(npmのパスを貼る)
$ npm install -g create-react-app # Reactのインストール

*必須ではないとなっているところは、筆者の環境上で必要なものだったコマンド
*事前にHomebrew をインストールしておく必要がある

npm へのパスがなかなかの貼れなくて、大変だった。

todo ディレクトリ内に React アプリを作成する。

terminal
$ pwd
/Users/hono/hoge/django_rest_framework/todo
$ create-react-app frontend
$ cd frontend
$ npm start

npm start を実行すると React の画面が表示される。

スクリーンショット 2018-09-24 22.51.46.png

■ Mockデータ

インストールした React を使って、Django の Model データを画面に表示させようとしている。
(Django はあくまで データを提供する API に徹する)

だがその前に、Mock データを使って React の画面表示のやり方を見てみよう。

編集するファイルは、frontend ディレクトリの src/App.js のみだ。

src/App.js
import React, { Component } from 'react';

// Mock データ
const list = [
    {
        id: 1,
        title: "1st todo",
        description: "Learn Django properly."
    },
    {
        id: 2,
        title: "Second item",
        description: "Learn Python."
    },
    {
        id: 3,
        title: "Learn HTTP",
        description: "It's important."
    }
]

class App extends Component {
    constructor (props) {
        super(props);
        this.state = { list };
    }

    render() {
        return (
            <div>
                {this.state.list.map(item => (
                    <div key={item.id}>
                        <h1>{item.title}</h1>
                        <p>{item.description}</p>
                    </div>
                ))}
            </div>
        );
    }
}

export default App;

list に Mock データを格納し、App コンポーネントに渡した。

それを map でループして、画面上に全てのデータを表示させる。

npm start している状態から localhost:3000 を web ブラウザで確認してみるとデータが表示されていることがわかる。

スクリーンショット 2018-09-25 6.00.37.png

■ アプリの連携(axios)

React 側の準備も整ったので、いよいよ Django と連携してみようと思う。

Django 側では、localhost:8000/api へアクセスするとデータを取得できるようにしてあるので、
ここに React 側から Get リクエストを送る必要がある。

axios を使うと REST API へのリクエスト処理を簡単に実装できるようになるらしい。

一旦、Control + c で React アプリを停止して、 axios をインストールする。

terminal
$ npm install axios
$ npm start

次は、App.js を以下のように書き換える。

src/App.js
import React, { Component } from 'react';
import axios from 'axios';

class App extends Component {
    state = {
        todos: []
    };

    componentDidMount() {
        this.getTodos();
    }

    getTodos() {
        axios
            .get('http://localhost:8000/api/')
            .then(res => {
                this.setState({ todos: res.data });
            })
            .catch(err => {
                console.log(err);
            });
    }

    render() {
        return (
            <div>
                {this.state.todos.map(item => (
                     <div key={item.id}>
                        <h1>{item.title}</h1>
                        <p>{item.body}</p>
                     </div>
                ))}
            </div>
        );
    }
}

export default App;

画面を表示してみる。

スクリーンショット 2018-09-25 6.28.53.png

うまくいったようだ。

■ Blog アプリ

Django REST Framework の機能をもっと使った Blog アプリを作成する。

具体的には、管理者以外のユーザの作成、権限、CRUD(Create,Read,Update,Delete)機能など。

■ セットアップ

初期設定をしよう。

terminal
$ pwd
/Users/hoge/Desktop/django_rest_framework
$ mkdir blogapi && cd blogapi
$ pipenv install django==2.1
$ pipenv shell
(blogapi-H-RPCYMU) bash-3.2$ django-admin startproject blog_project .
(blogapi-H-RPCYMU) bash-3.2$ python manage.py startapp posts

blog プロジェクトに posts アプリを作成したので、settings.py に登録して Django に認識させる。

settings.py
INSTALLED_APPS = [
    # Local
    'posts.apps.PostsConfig', # 追加

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

次は、マイグレーションを行う。

terminal
(blogapi-H-RPCYMU) $ python manage.py migrate

■ Model の設定

次は、Model を作成する。

Post のオブジェクトとして作成するので、投稿者、タイトル、本文、作成日、修正日あたりのカラムを持っていればいいと思う。

posts/models.py
from django.db import models
from django.contrib.auth.models import User


class Post(models.Model):
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    title = models.CharField(max_length=50)
    body = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

Model ができたら再びマイグレーションを行う。

terminal
(blogapi-H-RPCYMU) $ python manage.py makemigrations posts
(blogapi-H-RPCYMU) $ python manage.py migrate

■ 管理者の作成

管理者も作成しておこう。

posts/admin.py
from django.contrib import admin
from .models import Post

admin.site.register(Post)
terminal
(blogapi-H-RPCYMU) $ python manage.py createsuperuser
Username (leave blank to use 'hono'): admin
Email address: 
Password: 
Password (again): 
Superuser created successfully.
(blogapi-H-RPCYMU) $ python manage.py runserver

localhost:8000/admin/ にアクセスして、管理者と管理者ページが作成されたか確認する。

スクリーンショット 2018-09-26 6.05.48.png

Posts の [+ Add] をクリックして、何かデータを入力して、保存しよう。

スクリーンショット 2018-09-26 6.07.58.png

スクリーンショット 2018-09-26 6.08.11.png

■ Test コード

ちょっとだけ寄り道して、Post に対し、テストコードを書いてみる。

ログインユーザが、タイトルと本文を入力して、blog post を作れるか、みたいな感じのテストにする。

posts/tests.py
from django.test import TestCase
from django.contrib.auth.models import User

from .models import Post


class BlogTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        # Create a User
        testuser1 = User.objects.create_user(
            username='testuser1',
            password='abc123!'
        )
        testuser1.save()

        # Create a blog post
        test_post = Post.objects.create(
            author=testuser1,
            title='Blog title',
            body='Body content...'
        )
        test_post.save()

    def test_blog_content(self):
        post = Post.objects.get(id=1)
        expected_author = f'{post.author}'
        expected_title = f'{post.title}'
        expected_body = f'{post.body}'
        self.assertEquals(expected_author, 'testuser1')
        self.assertEquals(expected_title, 'Blog title')
        self.assertEquals(expected_body, 'Body content...')

ローカルサーバが動いていたら Ctrl + c で一旦止めて、terminal 上から以下のコマンドを押下してテストする。

(blogapi-H-RPCYMU) $ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.125s

OK
Destroying test database for alias 'default'...

Django のテストコードは、こんな感じで書くらしい。

■ Django REST Framework

今まで見てきた通り、Django REST Framework を追加しよう。

terminal
(blogapi-H-RPCYMU) $ pipenv install djangorestframework==3.8.2

次に、settings.py を書き換える。

blog_project/settings.py
INSTALLED_APPS = [
    # Local
    'posts.apps.PostsConfig',

    # 3rd-party apps
    'rest_framework', # 追加

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
# 追加
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ]
}

■ルーティング設定

ルーティングの設定をしよう。

blog_project/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('posts.urls')),
]

url の v1 は API の Version 1 を意味している。
*API として利用するので、こういう命名規則がベクトプラクティスらしい

アプリレベルの urls.py を作成し、ルーティングの設定を行う。

terminal
(blogapi-H-RPCYMU) $ touch posts/urls.py
posts/urls.py
# posts/urls.py
from django.urls import path

from .views import PostList, PostDetail


urlpatterns = [
    path('<int:pk>/', PostDetail.as_view()),
    path('', PostList.as_view()),
]

PostList と PostDetail は、あとで作成する。

■シリアライズ化

Model データを JSON 形式に変換する。

terminal
(blogapi-H-RPCYMU) $ touch posts/serializers.py
posts/serializers.py
from rest_framework import serializers
from .models import Post


class PostSerializer(serializers.ModelSerializer):

    class Meta:
        fields = ('id', 'author', 'title', 'body', 'created_at',)
        model = Post

■ view の設定

view の設定をする。

posts/views.py
from rest_framework import generics

from .models import Post
from .serializers import PostSerializer


# ListCreateAPIView -> read-write endpoint
class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer


# RetrieveUpdateDestoryAPIView -> ALlows read, update, delete
class PostDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

■ 動作確認

ここまで設定が完了したら localhost:8000/api/v1/ にアクセスして、実際の表示を確認しよう。

スクリーンショット 2018-09-28 6.13.04.png

PostList は、問題なく動作しているようだ。

localhost:8000/api/v1/1 にもアクセスする。

スクリーンショット 2018-09-28 6.14.43.png

PostDetail も問題なく動作している。

■ユーザーの追加

セキュリティは、Webサイトにとって重要なものだが、web API にとってはその 2 倍重要なものだ。(本に書いてあった)

全てのユーザーが、アイテムを作成、削除、更新、読み取りできてしまうのはよくない。

Django REST Framework では、様々なセキュリティを補佐する機能があるらしい。

テスト用にユーザーを追加しよう。

ローカルサーバを起動させて、localhost:8000/admin/ にアクセスしてから
Users で「+ Add」を押下する。

スクリーンショット 2018-09-29 5.42.22.png

testuser とパスワードを設定する。

スクリーンショット 2018-09-29 5.43.12.png

ユーザー詳細ページに遷移するが特に何も設定せずに SAVE ボタンを押下する。

スクリーンショット 2018-09-29 5.44.23.png

testuser が設定される。

スクリーンショット 2018-09-29 5.44.30.png

■ログインの実装

Django REST Framework でログイン機能を実装するのは簡単だ。

プロジェクトレベルの urls.py に以下を追加すればいい。

blog_project/urls.py
"""blog_project URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/2.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('posts.urls')),
    path('api-auth/', include('rest_framework.urls')),
]

ローカルサーバを起動して、localhost:8000/api/v1/ にアクセスすると、右上に「Log in」が表示されていることがわかる。

スクリーンショット 2018-09-30 7.38.17.png

■ アクセス権限

現時点で、Web API には、AllowAny(全てのユーザーがアクセス可能)の権限が振られている。

つまり、どのユーザーでもブログの投稿、削除、読み取り、更新ができてしまう。

認証済みのユーザーのみアクセスを許可したい場合は、アクセス権限を変更すればいい。

posts/views.py
from rest_framework import generics, permissions # 追加

from .models import Post
from .serializers import PostSerializer


# ListCreateAPIView -> read-write endpoint
class PostList(generics.ListCreateAPIView): # 追加
    permission_classes = (permissions.IsAuthenticated,)
    queryset = Post.objects.all()
    serializer_class = PostSerializer


# RetrieveUpdateDestoryAPIView -> ALlows read, update, delete
class PostDetail(generics.RetrieveUpdateDestroyAPIView): # 追加
    permission_classes = (permissions.IsAuthenticated,)
    queryset = Post.objects.all()
    serializer_class = PostSerializer

これでログイン権限のないユーザは、何もできなくなったはずだ。

localhost:8000/api/v1/1/ にアクセスしてみる。

スクリーンショット 2018-10-01 6.08.33.png

更新するためのリストが消えている。ちなみに、ログイン済みにすると下の画像のようになる。

スクリーンショット 2018-10-01 6.10.43.png

うまくいっているようだ。

しかし、View レベルの権限だと今後、View を増やすたびに似たような権限を追加していく必要があるので、少し煩雑である。

そこで、Django REST Framework には、Project レベルでアクセス権限を設定できる仕組みが用意されている。

項目 意味
AllowAny 認証有無に関わらず、全てのユーザーはアクセス許可
IsAuthenticated 認証・登録ずみのユーザーのみアクセス許可
IsAdminUser 管理者のみアクセス許可
IsAuthenticatedOrReadOnly 未認証のユーザは、閲覧権限のみ。認証済みのユーザは、書き込み、編集、削除権限をもつ

settings.py に IsAuthenticated 権限を付与してみる。

blog_project/settings.py
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}

また、views.py からさっき設定した権限を削除する。

posts/views.py
from rest_framework import generics

from .models import Post
from .serializers import PostSerializer


# ListCreateAPIView -> read-write endpoint
class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer


# RetrieveUpdateDestoryAPIView -> ALlows read, update, delete
class PostDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer

ブラウザをリフレッシュしてみるとログイン前の場合、更新がかけられなくなっていることがわかる。

スクリーンショット 2018-10-01 6.25.18.png

■ カスタムパーミッション

パーミッションをカスタマイズできるらしい。

terminal
(blogapi-H-RPCYMU) $ touch posts/permissions.py

カスタムパーミッションを作るには、Django REST Framework のBasePermission クラスの has_object_permission をオーバーライドする。

posts/permissions.py
from rest_framework import permissions


class IsAuthorOrReadOnly(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        # Read-only permissions are allowed for any request
        if request.method in permissions.SAFE_METHODS:
            return True

        # Write permissions are only allowed to the author of a post
        return obj.author == request.user

views.py も修正する。

posts/views.py
from rest_framework import generics

from .models import Post
from .permissions import IsAuthorOrReadOnly # 追加
from .serializers import PostSerializer


# ListCreateAPIView -> read-write endpoint
class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer


# RetrieveUpdateDestoryAPIView -> ALlows read, update, delete
class PostDetail(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = (IsAuthorOrReadOnly,) # 追加
    queryset = Post.objects.all()
    serializer_class = PostSerializer

ローカルサーバを起動して、localhost:8000/api/v1/1/ にアクセスしてみる。

・管理者
スクリーンショット 2018-10-02 6.23.10.png

・一般ユーザ
スクリーンショット 2018-10-02 6.22.56.png

■認証

Django REST Framework の認証について見てみよう。

DEFAULT_AUTHENTICATION_CLASSESで認証をかけられるようだ。

blog_project/settings.py
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [ # 追加
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ],
}

BasicAuthentication は、セッション ID を HTTP ヘッダに入れて、API に渡す役割を持ち、
SessionAuthentication は、ブラウザにログイン、ログアウトの機能を提供するらしい。

■認証トークン

先ほど実装した認証にトークンを利用できるようにする。

DEFAULT_AUTHENTICATION_CLASSES を以下のように修正する。

log_project/settings.py
REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication', # 変更
    ],
}

サーバー上でトークンを作成するために、authtoken app を Django に追加する。

log_project/settings.py
INSTALLED_APPS = [
    # Local
    'posts.apps.PostsConfig',

    # 3rd-party apps
    'rest_framework',
    'rest_framework.authtoken', # 追加

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

INSTALLED_APPS に変更を加えたら DATABASE と同期させるために、マイグレートを実施する。

もしローカルサーバーを起動している場合は、Ctrl + c で止めて、以下のコマンドを実行しよう。

terminal
(blogapi-H-RPCYMU) $ python manage.py migrate
(blogapi-H-RPCYMU) $ python manage.py runserver

http://localhost:8000/admin/ にアクセスすると「Tokens」の欄ができている。

スクリーンショット 2018-10-03 6.27.48.png

Tokens をクリックしても現在は何も設定されていないが、あとで使おう。

■ Django-Rest-Auth

django-rest-auth を使って、ログイン・ログアウトやパスワードのリセット機能を実装する。

ローカルサーバーを一旦止めて、terminal 上で、以下のコマンドを押下する。

terminal
(blogapi-H-RPCYMU) $ pipenv install django-rest-auth==0.9.3

次に、INSTALLED_APPS に django-rest-auth を追加する。

blog_project/settings.py
INSTALLED_APPS = [
    # Local
    'posts.apps.PostsConfig',

    # 3rd-party apps
    'rest_framework',
    'rest_framework.authtoken',
    'rest_auth', # 追加

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

これで、Django は、django-rest-auth を認識するので、ルーティングの設定をしよう。

blog_project/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('posts.urls')),
    path('api-auth/', include('rest_framework.urls')),
    path('api/v1/rest-auth/', include('rest_auth.urls')), # 追加
]

ローカルサーバーを動かして、localhost:8000/api/v1/rest-auth/login/ にアクセスしてみよう。

スクリーンショット 2018-10-04 5.53.33.png

うまくいっている。

localhost:8000/api/v1/rest-auth/logout/ にもアクセスする。

スクリーンショット 2018-10-04 5.54.08.png

これも大丈夫だ。

localhost:8000/api/v1/rest-auth/reset/ はどうだろうか。

スクリーンショット 2018-10-04 5.55.45.png

reset も大丈夫だ。

localhost:8000/api/v1/rest-auth/reset/confirm っていうのもある。

スクリーンショット 2018-10-04 5.56.59.png

たった数行のコードで、ログイン・ログアウト、リセット、リセット確認を実装できた。
もう、ゴリゴリコーディングする時代じゃないのかもしれない。

■ サインアップの実装

サインインとサインアップは名前は似ているが、役割が違う。

サインインは登録済みのユーザーの認証をに使われ、サインアップはユーザー登録と認証を同時にやる。

django-allauth を使って実装しよう。

ローカルサーバを停止して、terminal 上で、以下のコマンドをうつ。

(blogapi-H-RPCYMU) $ pipenv install django-allauth==0.37.1

インストールが完了したらいつも通り、INSTALLED_APPS に追加する。

blog_project/settings.py
INSTALLED_APPS = [
    # Local
    'posts.apps.PostsConfig',

    # 3rd-party apps
    'rest_framework',
    'rest_framework.authtoken',
    'allauth', # 追加
    'allauth.account', # 追加
    'allauth.socialaccount', # 追加
    'rest_auth',
    'rest_auth.registration', # 追加

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sites', # 追加
]

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
SITE_ID = 1

EMAIL_BACKENDは、ユーザーの登録した時に Email でも認証確認する時に使う機能だ。

SITE_ID は、sites フレームワークの一つで、いくつかの Django プロジェクトからたくさんの Web サイトをホストするための機能らしい。

今回は、どちらも使わないと思うが、一応推奨設定らしいので入れておく。

マイグレーションも行う。

terminal
(blogapi-H-RPCYMU) $ python manage.py migrate

最後にルーティングの設定を行う。

blog_project/urls.py
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('posts.urls')),
    path('api-auth/', include('rest_framework.urls')),
    path('api/v1/rest-auth/', include('rest_auth.urls')),
    path('api/v1/rest-auth/registration/',
         include('rest_auth.registration.urls')),
]

ローカルサーバを起動して、localhost:8000/api/v1/rest-auth/registration/ にアクセスする。

スクリーンショット 2018-10-05 6.02.32.png

登録画面が表示できたら、設定が成功している。

ちょっと前に認証トークンの設定を行ったと思う。
この画面でユーザーを新規登録すると認証トークンも発行されるようだ。

super_testuser を登録してみる。

スクリーンショット 2018-10-06 6.10.46.png

パスワードが短すぎて、怒られた。こういうチェック機能もついてるんだな。

スクリーンショット 2018-10-06 6.11.09.png

パスワードを長くしてみる。

スクリーンショット 2018-10-06 6.11.33.png

登録が成功するとトークンの Key が発行された。

スクリーンショット 2018-10-06 6.11.47.png

terminal を確認すると以下のように表示されている。

terminal
Content-Type: text/plain; charset="utf-8"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Subject: [example.com] Please Confirm Your E-mail Address
From: webmaster@localhost
To: sample@example.com
Date: Fri, 05 Oct 2018 21:11:38 -0000
Message-ID: <153877389860.996.13583794322804618519@onoakirBookpuro>

Hello from example.com!

You're receiving this e-mail because user super_testuser has given yours as an e-mail address to connect their account.

To confirm this is correct, go to http://localhost:8000/api/v1/rest-auth/registration/account-confirm-email/MQ:1g8XNq:lmi6ArNuZvT6rkybjEjvAF03BIc/

Thank you from example.com!
example.com

これは、django-allauth で作っているらしく、SMTP サーバーに設定を行うとメール通知ができるようになるらしい。

http://localhost:8000/admin/authtoken/token/ にアクセスしてみるとさっき登録した super_testuser が登録されていることがわかる。

スクリーンショット 2018-10-06 6.22.58.png

■ ユーザーリスト

Viewsetsrouters は、API の開発を加速させるツールだ。

これらを使って、既存のユーザの一覧をブラウザ上に表示させるAPIを作成する。(多分管理者には、必要な機能なんじゃないかな)

まずは、シリアライズクラスを作成する。

posts/serializers.py
from django.contrib.auth import get_user_model # 追加
from rest_framework import serializers
from .models import Post


class PostSerializer(serializers.ModelSerializer):

    class Meta:
        fields = ('id', 'author', 'title', 'body', 'created_at',)
        model = Post

# 追加
class UserSerializer(serializers.ModelSerializer):

    class Meta:
        model = get_user_model()
        fields = ('id', 'username',)

次は、view の設定を行う。

posts/views.py
from django.contrib.auth import get_user_model # 追加
from rest_framework import generics

from .models import Post
from .permissions import IsAuthorOrReadOnly
from .serializers import PostSerializer, UserSerializer # 追加


# ListCreateAPIView -> read-write endpoint
class PostList(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer


# RetrieveUpdateDestoryAPIView -> ALlows read, update, delete
class PostDetail(generics.RetrieveUpdateDestroyAPIView):
    permission_classes = (IsAuthorOrReadOnly,)
    queryset = Post.objects.all()
    serializer_class = PostSerializer

# 追加
class UserList(generics.ListCreateAPIView):
    queryset = get_user_model().objects.all()
    serializer_class = UserSerializer

# 追加
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
    queryset = get_user_model().objects.all()
    serializer_class = UserSerializer

最後に urls の設定を行う。

posts/urls.py
from django.urls import path

from .views import PostList, PostDetail, UserList, UserDetail


urlpatterns = [
    path('<int:pk>/', PostDetail.as_view()),
    path('', PostList.as_view()),
    path('users/', UserList.as_view()),
    path('users/<int:pk>/', UserDetail.as_view()),
]

設定が完了したらローカルサーバーを起動して、localhost:8000/api/v1/users/ にアクセスしよう。

スクリーンショット 2018-10-07 6.16.37.png

うまくいった。筆者の環境だと、5 ユーザーいる。

localhost:8000/api/v1/users/1 にアクセスすると詳細画面が見れる。

スクリーンショット 2018-10-07 6.18.25.png

Viewsets の機能は、これだけじゃないらしい。

■ View の統合

現時点での View は、PostList、PostDetail、UserList、UserDetail の 4 つある。

これを viewsets を使って統合する。

posts/views.py
from django.contrib.auth import get_user_model
from rest_framework import viewsets

from .models import Post
from .permissions import IsAuthorOrReadOnly
from .serializers import PostSerializer, UserSerializer


class PostViewSet(viewsets.ModelViewSet):
    permission_classes = (IsAuthorOrReadOnly,)
    queryset = Post.objects.all()
    serializer_class = PostSerializer


class UserViewset(viewsets.ModelViewSet):
    queryset = get_user_model().objects.all()
    serializer_class = UserSerializer

次は、ルーティングを変更する。
SimpleRouterを使えば、url を統合できるらしい。

posts/urls.py
from django.urls import path
from rest_framework.routers import SimpleRouter

from .views import UserViewset, PostViewSet


router = SimpleRouter()
router.register('users', UserViewset, base_name='users')
router.register('', PostViewSet, base_name='posts')

urlpatterns = router.urls

ローカルサーバーを立ち上げて、localhost:8000/api/v1/users/ にログインする。

スクリーンショット 2018-10-07 17.29.58.png

うまくいったようだ。

localhost:8000/api/v1/users/1 にアクセスして、詳細画面を表示してみる。

スクリーンショット 2018-10-07 17.31.29.png

こちらもうまくいっているみたいだが、先ほどとは表示のされ方が若干異なっている。

Post の方も見てみる。

localhost:8000/api/v1 にアクセスする。

スクリーンショット 2018-10-07 17.33.40.png

localhost:8000/api/v1/1 にアクセスする。

スクリーンショット 2018-10-07 17.33.52.png

うまくいっているようだ。

ちなみに、Post の方には、read only のパラメータをつけている。

permission_classes = (IsAuthorOrReadOnly,)

super_testuser は、一般ユーザー(superがついているけど)なので、詳細画面上で書き換えができないが、
管理者ユーザーの場合は、編集できるようになっている。

スクリーンショット 2018-10-07 17.35.03.png

■ API の見える化

Django REST Framework では、Core API を使って、API のスキーマーを自動的に作り出すことができるらしい。

早速インストールしよう。

ローカルサーバーを起動している場合は、Ctrl + c で止めてから、terminal 上で、以下のコマンドを実行する。

(blogapi-H-RPCYMU) $ pipenv install coreapi==2.3.3

インストールしたら、Django に Core API を認識させる。

blog_project/urls.py
from django.contrib import admin
from django.urls import include, path
from rest_framework.schemas import get_schema_view # 追加

schema_view = get_schema_view(title='Blog API') # 追加

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('posts.urls')),
    path('api-auth/', include('rest_framework.urls')),
    path('api/v1/rest-auth/', include('rest_auth.urls')),
    path('api/v1/rest-auth/registration/',
         include('rest_auth.registration.urls')),
    path('schema/', schema_view), # 追加
]

ここまで設定が完了したら、ローカルサーバーを立ち上げて、localhost:8000/schema/ にアクセスしよう。

スクリーンショット 2018-10-08 4.56.03.png

うまくいったようだ。

ただ、このスキーマーは、ちょっと読みにくいので、読みやすくする設定を行う。

blog_project/urls.py
from django.contrib import admin
from django.urls import include, path
from rest_framework.documentation import include_docs_urls # 追加
from rest_framework.schemas import get_schema_view

schema_view = get_schema_view(title='Blog API')

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('posts.urls')),
    path('api-auth/', include('rest_framework.urls')),
    path('api/v1/rest-auth/', include('rest_auth.urls')),
    path('api/v1/rest-auth/registration/',
         include('rest_auth.registration.urls')),
    path('docs/', include_docs_urls(title='Blog API')), # 追加
    path('schema/', schema_view),
]

localhost:8000/docs/ にアクセスしてみる。

スクリーンショット 2018-10-08 5.01.54.png

おお、なんかかっこよくなった。

blog_project/urls.py をちょっと整理する。

blog_project/urls.py
from django.contrib import admin
from django.urls import include, path
from rest_framework.documentation import include_docs_urls
from rest_framework.schemas import get_schema_view

API_TITLE = 'Blog API'
API_DESCRIPTION = 'A Web API for creating and editing blog posts.'
schema_view = get_schema_view(title=API_TITLE)

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('posts.urls')),
    path('api-auth/', include('rest_framework.urls')),
    path('api/v1/rest-auth/', include('rest_auth.urls')),
    path('api/v1/rest-auth/registration/',
         include('rest_auth.registration.urls')),
    path('docs/', include_docs_urls(title=API_TITLE,
                                    description=API_DESCRIPTION)),
    path('schema/', schema_view),
]

もう一度、localhost:8000/docs/ にアクセスする。

スクリーンショット 2018-10-08 5.07.37.png

■ Django REST Swagger

他にも Django REST Swagger という便利なパッケージがある。

ローカルサーバーを一旦止めて、以下をインストールする。

terminal
(blogapi-H-RPCYMU) $ pipenv install django-rest-swagger==2.2.0

そして、いつもの通り、Django に認識させる。

blog_project/settings.py
INSTALLED_APPS = [
    # Local
    'posts.apps.PostsConfig',

    # 3rd-party apps
    'rest_framework',
    'rest_framework.authtoken',
    'rest_framework_swagger', # 追加
    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'rest_auth',
    'rest_auth.registration',

    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.sites',
]

blog_project/urls.py を書き換える。

blog_project/urls.py
from django.contrib import admin
from django.urls import include, path
from rest_framework.documentation import include_docs_urls
from rest_framework.schemas import get_schema_view
from rest_framework_swagger.views import get_swagger_view # 追加

API_TITLE = 'Blog API'
API_DESCRIPTION = 'A Web API for creating and editing blog posts.'
# schema_view = get_schema_view(title=API_TITLE) # 追加
schema_view = get_swagger_view(title=API_TITLE) # 追加

urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/v1/', include('posts.urls')),
    path('api-auth/', include('rest_framework.urls')),
    path('api/v1/rest-auth/', include('rest_auth.urls')),
    path('api/v1/rest-auth/registration/',
         include('rest_auth.registration.urls')),
    path('docs/', include_docs_urls(title=API_TITLE,
                                    description=API_DESCRIPTION)),
    # path('schema/', schema_view), # 追加
    path('swagger-docs/', schema_view), # 追加
]

ローカルサーバーを起動して、localhost:8000/swagger-docs/ にアクセスする。

スクリーンショット 2018-10-08 5.20.51.png

おお、すごい。

こういうの現場でガンガン入れたいな、と思いました。

■ 参考

Djang REST Framework Quickstart

■ まとめ

Django Rest Framework のチュートリアルを見てみると結構、色々な機能がある。

まだまだ勉強しないといけないことがあるようだ。