Help us understand the problem. What is going on with this article?

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
api/serializers.py
from rest_framework import serializers

from books.models import Book


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = Book
        fields = ('title', 'subtitle', 'author', 'isbn')

■ 動作確認

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

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/password/reset/ はどうだろうか。

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

reset も大丈夫だ。

http://localhost:8000/api3/v1/rest-auth/password/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 のチュートリアルを見てみると結構、色々な機能がある。

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

__init__
PythonとGo言語が一番好きです。どちらも仕事で使っています!
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした