DjangoでRest Apiを作成する方法

#はじめに
DjangoでRESTAPIを作る方法を学んだので、深く理解するために
さらに肉付けしてアウトプットします。
__今回環境構築の説明は省きます。__
__以下の記事で構築した環境を使っているので、必ず参照ください。__
[Anacondaで仮想環境を作り、PyCharmと紐付ける。](https://qiita.com/hayashi0207/items/8bdc0c232c4203c92d6f)
またあらゆる任意の名前は上記の記事で利用したものを前提にしていますので、あらかじめご了承ください。

#事前準備&環境
・Crome ModHeader
・Postman
・Anaconda Navigator
・PyCharm CE
・MacOS

#目次
1.モデルの作成
2.シリアライザーの作成
3.ビューの作成
4.動作確認
5.おまけ：APIをフロントエンドから利用できるように設定

#1.モデルの作成
モデルとはデータベースの設計、作成、操作（いわゆるORM）までをPythonでできるものです。
テーブル名はclass,カラムmodelsで提供されているフィールドを使用して定義していきます。
データ作成時間はauto_now_add=True、更新時間はauto_now=Trueを指定することでそれぞれの値を取得することができます。

```python:models.py
from django.db import models

# Create your models here.
#データベース設計
class Todo(models.Model):
    title = models.CharField(max_length=50)
    content = models.CharField(max_length=400)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
```

作成したモデルをmakemigrationsし、migrateします。
もちろん、ちゃんと説明します。
・makemigrations：データベースのバージョン管理をするための__マイグレーションファイル__を作成する。
・migrate：データベース構成を変更したり、その変更を取り消したりする。

```terminal
$ python manage.py makemigrations
$ python manage.py migrate
```
以上でモデルの作成とmigrateは完了です。

#2.シリアライザーの作成
シリアライザーとは、バリデーションやパスワードのハッシュ化などデータを適切な形に変えてモデルに入出力を行うものです。
入力フォームからデータベースを扱うモデルへ橋渡しをするような役割を担います。
api直下に新しくserializers.pyを作成します。
この章でのトークンとは認証トークンのことであり、
認証トークンとはユーザを一意に認識するための情報（文字列）とイメージして大丈夫です。

```python:serializers.py
from rest_framework import serializers
from .models import Todo
#djangoのデフォルトで用意されているUserモデルをインポート
from django.contrib.auth.models import User
#ユーザ用のトークンをインポート
from rest_framework.authtoken.models import Token

class UserSerializer(serializers.ModelSerializer):
    #基本設定を行うクラス
    class Meta:
        model = User
        fields = ('id', 'username', 'password')
        #passwordはGETでアクセスできない上入力必須に指定する
        extra_kwargs = {'password': {'write_only':True, 'required': True}}

    #ユーザを作る際に使用するcreateメソッドをオーバーライドする。
    def create(self,validated_data):
        #パスワードをハッシュ化する
        user = User.objects.create_user(**validated_data)
        #トークンを生成する
        Token.objects.create(user=user)
        return user

class TodoSerializer(serializers.ModelSerializer):

    #djangoのDatetimefieldの表記を変更
    created_at = serializers.DateTimeField(format="%Y-%m-%d %H:%M", read_only=True)
    updated_at = serializers.DateTimeField(format="%Y-%m-%d %H:%M", read_only=True)

    class Meta:
        model = Todo
        fields = ['id','title','content','created_at','updated_at']
```

これでシリアライザは作成できました。

#3.ビューの作成
ビューとは、データのアクセス権や認証を制御するものです。
正しく認証されたユーザのみCRUDが扱えるようにする役割を担っています。
viewsを作成する前に、permissionファイルを作成します。
Userモデルでは、PUTとDELETEができないようにするためです。
api直下にownpermissionsを作成しGET,POSTのみを受け付けるように指定します。

```python:ownpermissions
from rest_framework import permissions

class OwnPermission(permissions.BasePermission):

    def has_object_permission(self, request, view, obj):
        #SAFE_METHODでGET,POSTのみ許容
        if request.method in permissions.SAFE_METHODS:
            return True
        return False
```

次にビューを作成します。

```python:views.py
from django.shortcuts import render

#トークン認証に必要なライブラリ
from rest_framework.authentication import TokenAuthentication
from rest_framework.permissions import IsAuthenticated

#ビュー作成に必要なライブラリ
from rest_framework import generics
from rest_framework import viewsets

#作成したモデルとシリアライザをインポート
from django.contrib.auth.models import User
from .models import Todo
from .serializers import UserSerializer,TodoSerializer

#作成したpermissionをインポート
from .ownpermissions import OwnPermission

class UserViewSet(viewsets.ModelViewSet):

    #ユーザオブジェクトを全て取得する
    queryset = User.objects.all()

    #使用するシリアライザを指定する
    serializer_class = UserSerializer

    #誰でも見れるように指定する
    permission_classes = (OwnPermission,)

class ManageUserView(generics.RetrieveUpdateAPIView):
    serializer_class = UserSerializer

    #認証が通ったユーザのみアクセスできるように指定する
    authentication_classes = (TokenAuthentication,)

    #ログインしているユーザのみ許可するように指定する
    permission_classes = (IsAuthenticated,)

    #ログインしているユーザ情報を返す関数
    def get_object(self):
        return self.request.user

class TodoViewSet(viewsets.ModelViewSet):
    queryset = Todo.objects.all()
    serializer_class = TodoSerializer
    authentication_classes = (TokenAuthentication,)
    permission_classes = (IsAuthenticated,)
```

これでビューは完成です。

urlsも作成しましょう。
urlsはビューとパスを紐づける役割を担います。
プロジェクトとアプリ両方に以下の編集を加えます。（アプリの方は新規作成）

```python:drfapi/urls
from django.contrib import admin
from django.urls import path
from django.conf.urls import include
from rest_framework.authtoken.views import obtain_auth_token

urlpatterns = [
    path('admin/', admin.site.urls),
    #apiのurlsを見にいく
    path('api/',include('api.urls')),
    #ユーザ名とパスワードをPOSTするとトークンを返す。
    path('auth/',obtain_auth_token ),
]
```
genericsから継承したモデルはas_view( )を使ってviewとパスを紐づけます。
    
```python:api/urls
from django.urls import path
from django.conf.urls import include
from rest_framework import routers
from api.views import UserViewSet, ManageUserView, TodoViewSet

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

urlpatterns = [
    path('myself/',ManageUserView.as_view( ), name='myself'),
    #ユーザ名とパスワードをPOSTするとトークンを返す。
    path('',include(router.urls)),
]
```
#6.動作確認
まずadminのダッシュボードでモデルが正しく動作するのかを確認します。
models.pyにタイトル名を返す関数、admin.pyにはTodoをadminに認識させるコードを記述します。

```python:models.py
def __str__(self):
   return self.title
```
```python:admin.py
from django.contrib import admin
from .models import Todo
#adminにTodoを登録
admin.site.register(Todo)
```
adminのダッシュボードにログインするにはsuperユーザ（権限を持つユーザ）が必要です。
superユーザ（権限を持つユーザ）はターミナルから作成します。

以下のコマンドを入力すると応答が返ってくるので、name,email,passwordを入力します。
emailは未入力でも構いません。
簡単なパスワードだと警告が出てきますが問題なければyを入力しましょう。

```terminal
$ python magange.py createsuperuser
Username (leave blank to use ホスト名): admin
Email address: 
Password: 
Password (again): 
The password is too similar to the username.
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.
```
ではadminのダッシュボードを使っていきます。
manage.pyを実行してローカルホストのURLに「/api/users」を追記し、アクセスします。
ユーザ名とパスワードを入力して、ログインします。
![スクリーンショット 2020-09-26 23.06.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703431/72418662-313c-668b-7226-b16b028cec82.png)
ログインできたらTodosの[ADD TODO]からタイトルとコンテンツを入力しましょう。
![スクリーンショット 2020-09-26 23.15.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703431/70e10dbc-6ce7-5aef-2927-eb5e0cead177.png)
以下の画像のように保存できればモデルは正常に動作しています。
![スクリーンショット 2020-09-26 23.16.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703431/aa471f5d-8297-3f51-f748-2d973fdbe486.png)

次にPostmanを使ってCRUDの動作確認をします。
先ほどと同様にmanage.pyを実行している状態でローカルホストのURLに「/api/users」を追記し、アクセスします。
ユーザ名とパスワードを入力してPOSTし、改めてGETします。
以下の画像のように値が更新されていればUSERのGET,POST通信は成功です。
![スクリーンショット 2020-09-26 23.22.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703431/e4fc14d9-69a1-b35f-5c08-07259d8aae96.png)

次はTodolistにアクセスしてみましょう。
URLの「users」の部分を「todolist」を変換し、アクセスします。
![スクリーンショット 2020-09-26 23.28.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703431/eff0523e-7b89-5ced-8e1b-f05ed47423a9.png)
認証されたユーザでないとCRUDの権限がありませんと警告されてしまいました。
これはユーザ認証が必要とviews.pyで記述したためです。

認証トークンを使ってアクセスします。
認証トークンはPostmanから取得します。

Postmanを起動します。
起動したら白い「＋」ボタンを押します。
![スクリーンショット 2020-09-27 9.20.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703431/d27dd6fd-15fc-0f9c-13c7-fcf0076d4b95.png)
「auth」を追記したURLを入力します。HttpリクエストはPOSTを選択してください。
Body>form-dataからユーザ名とパスワードの項目を作成して、存在するVALUEを入力します。
SENDを押すと、トークンが返ってくるのでコピーします。
![スクリーンショット 2020-09-27 9.32.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703431/a5dff4f1-3e00-2445-6f43-b6994699a1ab.png)
ブラウザからcrome ModHeader を起動します。
「＋」ボタンを押して
[Authorization]を選択して[Token 「コピーしたトークン」]を入力します。(Tokenの後に半角スペース)
![スクリーンショット 2020-09-27 9.36.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/703431/7b926160-5b92-fdc5-ecbc-d113a6ce5ca1.png)
これでTodolistにもアクセスすることができるようになります。

#5.おまけ：APIをフロントエンドから利用できるように設定
CORSをインストールします。
CORSとはHTTPヘッダーの転送で構成されるシステムであり、ブラウザがオリジンをまたいだリクエストのレスポンスに、フロントエンドのJavaScriptコードがアクセスすることをブロックするかどうかを決めるものです。
ちなみにオリジンとは、リソース自身のURLの「スキーム（プロトコル）」「ホスト（ドメイン）」「ポート」の組み合わせのことです。

```terminal
$ pip install django-cors-headers
```
DjangoにCORSを認識させます。

```python:setting.py
INSTALLED_APPS = [
   #<略> 
   'corsheaders',
]

MIDDLEWARE = [
   #<略>
   'corsheaders.middleware.CorsMiddleware',
]

CORS_ORIGIN_WHITELIST = [
    'http://localhost:3000',
]
```

これでフロントエンドからアクセスできるようになりました。
次の記事では、フロント側からAPIにアクセスする方法をまとめたいと思います。

#参考文献
・Djangoの概要（https://docs.djangoproject.com/ja/3.1/intro/overview/）
・Django REST APIでデバイス管理ツールを作ってみよう（https://www.udemy.com/course/django-rest-api/）
・【学習メモ】MakemigrationsとMigrateについて（https://qiita.com/frosty/items/8c715a53d7920c9cd1eb）
・Django REST frameworkで学んだことをまとめてみた（https://qiita.com/breakthrough/items/f845592961d8863a72c5）
・MDN（https://developer.mozilla.org/ja/docs/Glossary/CORS）