【初心者向け】Python 3.7 + Django で RESTful Web API をサクッと作る方法

## 前提
以下の環境で構築した。

- マシン：MacBook Pro (Retina, 15-inch, Mid 2015)
- OS：macOS High Sierra (v10.13.6)
- Python v3.7.0 ⇨ `python3` コマンドで実行（Mac なので）
- pip v18.1 ⇨ pip は `pip3` コマンドで実行。`pip3 install --upgrade pip` で最新にしたら `pip` で打てた（ちなみに Windows は `python -m pip install --upgrade pip` のよう）

上記のミドルウェア環境が未構築の場合は、公式のインストーラなど適当に使ってインストールしてください。

## 手順

※ macOS なので `python` を `python3` と打っています。各自で読み替えてください。

#### pip で必要パッケージをインストール

まず適当なディレクトリを作成してターミナルを開き、pip で以下のようにして必要なパッケージをインストールする

```bash
pip install 各パッケージ名
```

ダウンロード指定する必要なパッケージは以下

|パッケージ|用途|
|:--|:--|
|django|Django フレームワーク|
|djangorestframework|Django REST フレームワーク|
|django-filter|Django REST F/W に検索機能を楽に追加できるプラグイン（※ 結局、使ってないので後で調べて追記します）|
|django-cors-headers|Django REST F/W で作成した API をブラウザからも叩けるようにCORSに対応させるプラグイン|

#### pip 設定ファイル作成

インストールパッケージ情報を、後で別環境でも一括でインストールできるように、設定ファイルとして残しておく

```bash:設定ファイル出力
pip freeze > requirements.txt
```
作成された設定ファイルは、バージョン明示指定されているので、必要あれば `==バージョン` を削除しておくこと（消さなくても良い）

#### プロジェクトを作成

```bash:プロジェクト作成
django-admin startproject demo_app
```
ディレクトリ内に demo_app が出来あがる。
この時、結果的に同じ名前のディレクトリが入れ子で２つ作られるが、一番上の `demo_app` はプログラムに影響ないので自由に変えていいやつ。中にできた `demo_app` はプログラムに影響するので安易に変えたらいかんやつ。

#### アプリケーションを作成

作成したプロジェクト内に移動してから、アプリケーションを作成する

```bash:アプリケーション作成
cd demo_app
python3 manage.py startapp sample_app
```
#### データベースを作成

DB設定 (setting.py) はデフォルト (SQLite を使用) のまま特に変更なし
ちなみに設定されている箇所は以下

```py:demo_app/demo_app/settings.py
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}
```
データベースを作成する

```bash
python3 manage.py migrate
```

※ これやらないと、アプリ起動時に django_session テーブルを探しにいくタイミングでエラーになります。

#### 一時的なファイルはバージョン管理から外す (任意作業)

以下の作業は、必要だと思う人だけやってください。やらなくてもいい。

Git でバージョン管理をしてて、マイグレーションで生成されるものを対象から外したいなら、
.gitignore を新規で作成し、以下の通りに中身を記載

```bash:demo_app/.gitignore
# Python cache files
__pycache/
*.pyc

# SQLite
db.sqlite3
```

#### Webアプリをテスト起動

3000番ポートでWebアプリを起動してみる

```bash
python3 manage.py runserver 3000
```

コンソールでアプリ起動状態を確認後、Web ブラウザで `http://localhost:3000` にアクセスすると下図のようになる
<img width="1056" alt="python_djangoアプリテスト起動.png" src="https://qiita-image-store.s3.amazonaws.com/0/199602/ae50ed68-871e-85ed-d851-d8352a387c27.png">

起動成功したら `Ctrl` + `C` でアプリを終了しておく

#### アプリケーションにモデルを作成

作成したアプリケーションのモデルにクラスを追記する

```py:demo_app/sample_app/models.py
from django.db import models

### ここから下を追加 ###
class Test(models.Model):
    title = models.CharField(max_length=140, blank=False)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title
```

#### setting.py にアプリケーションを追加する
```py:demo_app/demo_app/settings.py
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'sample_app', # 作成したアプリケーションを追加
]
```

#### DBにアプリ設定の変更を適用

モデル変更を適用するためのマイグレーションを作成

```bash
python3 manage.py makemigrations
```

モデル変更を DB に適用

```bash
python3 manage.py migrate
```

#### 管理者ユーザを作成

```bash
python3 manage.py createsuperuser
```

`Username`, `Email address`, `Password` は適当に決める。
パスワードがセキュリティ低い内容だと `Bypass password validation and create user anyway?` と出るが、 "yes" でそのまま作れる。

#### 管理者ページを作成

```py:demo_app/sample_app/admin.py
from django.contrib import admin

### ここから下を追加 ###
from .models import Test

admin.site.register(Test)
```

#### Webアプリを再びテスト起動

3000番ポートでWebアプリを起動してみる

```bash
python3 manage.py runserver 3000
```

Web ブラウザで `http://localhost:3000/admin/` にアクセスするとログイン画面になる

<img width="1400" alt="python_django管理画面_01.png" src="https://qiita-image-store.s3.amazonaws.com/0/199602/d2e196a7-2038-e106-cdcf-db7ea20d8ec1.png">

先ほど設定した管理者ユーザ情報でログインすると、下図のようになる

<img width="1402" alt="python_django管理画面_02.png" src="https://qiita-image-store.s3.amazonaws.com/0/199602/989f9c41-f92c-c2cc-80cc-f6736a653eff.png">

成功したら `Ctrl` + `C` でアプリ終了

#### アプリをローカライズしておく

アプリの言語を日本語に、タイムゾーンを「アジア/東京」に変更しておく
setting.py に以下の通りに修正する

```py:demo_app/demo_app/settings.py
# Internationalization
# https://docs.djangoproject.com/en/2.1/topics/i18n/

LANGUAGE_CODE = 'ja' # 'en-us' を修正
TIME_ZONE = 'Asia/Tokyo' # 'UTC' を修正
USE_I18N = True
USE_L10N = True
USE_TZ = True
```

#### Webアプリを再びテスト起動

3000番ポートでWebアプリを起動してみる

```bash
python3 manage.py runserver 3000
```

`http://localhost:3000/admin/` にアクセスするとログイン画面が日本語になってる

<img width="1401" alt="python_django管理画面_日本語化.png" src="https://qiita-image-store.s3.amazonaws.com/0/199602/262484e5-5faf-e291-842d-1b6de24ad3af.png">

成功したら `Ctrl` + `C` でアプリ終了

#### REST API フレームワークを導入

setting.py に Django REST Framework を追加する

```py:demo_app/demo_app/settings.py
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework', # 追記
    'sample_app',
]
```

#### モデルの REST API 定義を作成

モデルに対する REST API を作成するために以下の定義を作成する

|作成する定義|用途|
|:--|:--|
|Serializer|モデルのシリアライズ/デシリアライズ（データの入出力）方式を定義|
|ViewSet|取得, 一覧, 登録, 更新, 削除を管理するビュー定義|
|URL pattern|DjangoにURLパターンを伝えるための定義|

##### Serializer 定義ファイルを作成

アプリケーション ディレクトリ配下に `serializer.py` を作成し、以下の内容を書く

```py:demo_app/sample_app/serializer.py
from rest_framework import serializers

from .models import Test

class TestSerializer(serializers.ModelSerializer):
  class Meta:
    model = Test
    fields = ('id', 'title', 'created_at')

```

##### ViewSet 定義ファイルを作成

アプリケーション ディレクトリ配下の `views.py` を修正する

```py:demo_app/sample_app/views.py
from django.shortcuts import render

### ここから下を追加 ###
import django_filters
from rest_framework import viewsets, filters
from .models import Test
from .serializer import TestSerializer

class TestViewSet(viewsets.ModelViewSet):
    queryset = Test.objects.all().order_by('-created_at')
    serializer_class = TestSerializer
```

##### URL pattern 定義ファイルを作成

プロジェクト名と同名の既存ディレクトリ配下にある `urls.py` を以下の通り修正する

```py:demo_app/demo_app/urls.py
from django.contrib import admin
from django.urls import path, include # include を追加

from sample_app.urls import router as sample_app_router # 追加

urlpatterns = [
    path(r'admin/', admin.site.urls), # 修正
    path(r'api/', include(sample_app_router.urls)), # 追加
]
```

アプリケーション ディレクトリ配下に `urls.py` を新たに作成する

```py:demo_app/sample_app/views.py
from rest_framework import routers
from .views import TestViewSet

router = routers.DefaultRouter()
router.register(r'sample', TestViewSet)
```

#### Webアプリを再びテスト起動

3000番ポートでWebアプリを起動してみる

```bash
python3 manage.py runserver 3000
```

Web ブラウザで `http://localhost:3000/api/` にアクセスすると以下の画面になる

<img width="1407" alt="python_django_REST_API画面.png" src="https://qiita-image-store.s3.amazonaws.com/0/199602/f05f4f63-2b0d-8d16-38c0-123ebcdd3edd.png">

成功したら `Ctrl` + `C` でアプリ終了

この時点で、`http://localhost:3000/admin/`, `http://localhost:3000/api/` しか許可していないので、`http://localhost:3000/` には行けなくなっています。これで API としてのアプリ (＋管理画面) になりました。

※ アクセス拒否等のエラーが発生した場合は、「markdown」のバージョンが古い可能性があるので、ターミナル等からmarkdown のバージョンバージョンアップしてみてください。

#### API をブラウザなどから呼べるように CORS 許可対応しておく

setting.py に以下の通りに追加する

```py:demo_app/demo_app/settings.py
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'corsheaders', # 追記
    'sample_app',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'corsheaders.middleware.CorsMiddleware', # 追記 (CommonMiddleware より前に)
    'django.middleware.locale.LocaleMiddleware', # 追記 (CommonMiddleware より前に)
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

... # (中略)

# CORS 対応
CORS_ALLOW_CREDENTIALS = True # 追記 (ファイル末尾)
CORS_ORIGIN_ALLOW_ALL = True # 追記 (ファイル末尾)

```

以上。あとはよしなにいじってください。（後でサンプルコード等を追記するかも。取り急ぎ備忘録で記載）