DjangoとReactでPDCAアプリを作る その2

##まず私について
初めまして!

kenshoと申します。
2019年からプログラミングの勉強を本格的に始めて、web制作フリーランスを経験、その後はIT企業のインターン生として働きながら独学でwebエンジニアの勉強をしてきました。
2021年4月から新卒で上場企業のwebエンジニアとして働く予定です。

よかったらTwitterもやっておりますので気軽にフォローお願いします♪

Twitterアカウント↓
[健将@WEBエンジニア×明大生](https://twitter.com/kenshow_study)
##このアプリ開発記事

[DjangoとReactでPDCAアプリを作る その1](https://qiita.com/kenshow-blog/items/0c3d8fa91dd94b4e9fe0)

[DjangoとReactでPDCAアプリを作る その3](https://qiita.com/kenshow-blog/items/8e7c32eafc7ad87c3e0a)

[DjangoとReactでPDCAアプリを作る その4](https://qiita.com/kenshow-blog/items/c062312de96cbb084643)

##前回までのあらすじ

前回は、このPDCAアプリのサーバーサイドの認証部分を作成した
↓↓
[DjangoとReactでPDCAアプリを作る その1](https://qiita.com/kenshow-blog/items/0c3d8fa91dd94b4e9fe0)

ここまでで
django-rest-frameworkを用いたユーザー認証、登録のAPIが完成している。

##今回やること

今回は、このアプリの主機能となるPDCA機能のAPIをdjango-rest-frameworkを利用して開発していく。
git⬇️
https://github.com/kenshow-blog/workapplication

参考画像↓

まずは、jwtトークンを発行する（事前にユーザーは登録済み）
![スクリーンショット 2021-03-18 10.42.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1037585/62932e5b-5ba9-24d8-48c4-b5715ddfbd87.png)

アクセストークンが発行される
![スクリーンショット 2021-03-18 10.43.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1037585/3f711642-0475-649e-76ef-bf26460f0138.png)

googleの拡張機能である
[ModHeader](https://chrome.google.com/webstore/detail/modheader/idgpnmonknjnojddfkpgkljpfnnfcklj?hl=ja)を利用して、こちらのアクセストークンをヘッダーに付ける

PDCリストにアクセス↓
![スクリーンショット 2021-03-18 10.44.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1037585/c26eed81-1d30-a67f-cc03-753675287447.png)

ACTIONリストにアクセス↓
![スクリーンショット 2021-03-18 10.44.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1037585/06212b9a-0851-f81f-81b9-b4e798644733.png)


###なぜPDCとA(アクション)を分けたのか？

アクションの使い勝手を柔軟にしておきたかったから。
ログイン後,HOME画面へ遷移するのだが、その際に、アクション（自分が日々の記録の中で発見した改善するべき点）の一覧を表示できるようにしときたかった。
また、別にカテゴリーを設けて、カテゴリーごとにアクションを表示できるようにもなる。

もちろんPDCAでまとめて作成してアクションだけを自由に使うこともできたのだが、PDCの部分は詳細ページのところくらいでしか利用する機会がないため、アクションだけ独立させてしまった方が良いかなと考えた。


また、APIがちゃんと機能しているかを確かめる際に、<strong>POSTMAN</strong>というツールを使っていました。
これで、リクエストを投げることもできるし、期待通りのレスポンスが返ってくるかもテストすることができます。

参考記事↓
[API開発・テスト便利ツール Postmanの使い方メモ](https://qiita.com/zaburo/items/16ac4189d0d1c35e26d1)


##DjangoでPDCAのAPIを開発する

ここからいよいよ、PDCAのAPIの開発に移っていく

###models.pyを作成

```python:pdca/models.py
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MinValueValidator
import uuid
# Create your models here.


class Pdc(models.Model):
     #uuidでidを複雑に作成できるようにしておく
    id = models.UUIDField(default=uuid.uuid4, primary_key=True, editable=False)
    userPdc = models.ForeignKey(User, on_delete=models.CASCADE, related_name='userPdc')
    title = models.CharField(max_length=100)#その日作成するPDCAのタイトル
    plan = models.CharField(max_length=400)
    do = models.CharField(max_length=400,null=True,blank=True)
    check = models.CharField(max_length=400,null=True,blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title


class Category(models.Model):
    item = models.CharField(max_length=100)

    def __str__(self):
        return self.item

class Action(models.Model):
    action = models.CharField(max_length=200, null=True,blank=True)
    pdca = models.ForeignKey(Pdc, on_delete=models.CASCADE, related_name="pdcs")
    action_user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="action_user")
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.action

```

####nullとblankの違いについて
実はここの違いが曖昧だったがために、少しだけつまづいてしまったので、ここで共有しておきます。😅

要点は下記にまとめました

---------
modelsでCharFieldにて、中身が空欄で保存されても構わない場合、<strong>null=TrueだけでなくblankもTrueに設定しておく</strong>
nullはデータベースに保存される時はnullでも構わないということであり、入力フォームがblankであることを許可する役割は持っていないため

-----

PDCAというのは基本、新規でPDCAを作成する時、PLANの内容を先に書いておいて一旦保存、そしてその日の終わりに、do,check,actionを書いていく。
故に、新規作成の時点では、plan以外の項目は作成されないケースが多いので、空欄で保存しても大丈夫なように設定しておいた。

admin.pyに作成したモデルを登録していく

```python:pdca/admin.py
from django.contrib import admin
from .models import Pdc, Action, Category
# Register your models here.
admin.site.register(Category)
admin.site.register(Pdc)
admin.site.register(Action)
```

makemigrations,migrateをした後、serializers、viewsを作成していく

###serializers.pyを作成する

```python:pdca/serializers.py
from rest_framework import serializers
from .models import Pdc, Action, Category

class CategorySerializer(serializers.ModelSerializer):

    class Meta:
        model = Category
        fields = ['id', 'item']


class PdcSerializer(serializers.ModelSerializer):

    created_at = serializers.DateTimeField(
        format="%Y/%m/%d", read_only=True
    )
    updated_at = serializers.DateTimeField(
        format="%Y/%m/%d", read_only=True
    )
    class Meta:
        model = Pdc
        fields = ['id', 'userPdc', 'title', 'plan', 'do',
                    'check', 'created_at', 'updated_at']
        extra_kwargs = {'userPdc': {'read_only': True}}

class ActionSerializer(serializers.ModelSerializer):
    category_item = serializers.ReadOnlyField(
        source='category.item', read_only=True
    )
    created_at = serializers.DateTimeField(
        format="%Y/%m/%d", read_only=True
    )
    updated_at = serializers.DateTimeField(
        format="%Y/%m/%d", read_only=True
    )

    class Meta:
        model = Action
        fields = ['id', 'action', 'pdca', 'action_user','category', 'category_item',
                    'created_at', 'updated_at']

        extra_kwargs = {'action_user': {'read_only': True}}
    
```

作成日、更新日は、ここの時点で形を整えて、そのままフロント側で表示させられるようにしておいた。
また、これらは、意図的に編集されるものではなく自動で登録されるものなので、read_onlyにしておいた。

category_itemを設定することで、actionリストで各アクションに紐付けられたカテゴリーの内容を、表示できるようにした。
というのも、actionデータの中でカテゴリーの内容を直接参照できれば、フロントで表示させる際に実装がかなり楽になるからである。

###views.pyを作成する

```python:pdca/views.py
from rest_framework import status, viewsets
from .serializers import CategorySerializer, PdcSerializer, ActionSerializer
from .models import Category, Pdc, Action
from rest_framework.response import Response
# Create your views here.

class CategoryViewSet(viewsets.ModelViewSet):
    queryset = Category.objects.all()
    serializer_class = CategorySerializer

    def destroy(self, request, *args, **kwargs):
        response = {'message': 'DELETE method is not allowed'}
        return Response(response, status=status.HTTP_400_BAD_REQUEST)

    def update(self, request, *args, **kwargs):
        response = {'message': 'UPDATE method is not allowed'}
        return Response(response, status=status.HTTP_400_BAD_REQUEST)
        
    def partial_update(self, request, *args, **kwargs):
        response = {'message': 'PATCH method is not allowed'}
        return Response(response, status=status.HTTP_400_BAD_REQUEST)


class PdcViewSet(viewsets.ModelViewSet):
    queryset = Pdc.objects.all()
    serializer_class = PdcSerializer

    def get_queryset(self):
        return self.queryset.filter(userPdc=self.request.user)

    def perform_create(self, serializer):
        serializer.save(userPdc=self.request.user)

    def partial_update(self, request, *args, **kwargs):
        response = {'message': 'PATCH method is not allowed'}
        return Response(response, status=status.HTTP_400_BAD_REQUEST)
    
class ActionViewSet(viewsets.ModelViewSet):
    queryset = Action.objects.all()
    serializer_class = ActionSerializer

    def get_queryset(self):
        return self.queryset.filter(action_user=self.request.user)

    def perform_create(self, serializer):
        serializer.save(action_user=self.request.user)

    def partial_update(self, request, *args, **kwargs):
        response = {'message': 'PATCH method is not allowed'}
        return Response(response, status=status.HTTP_400_BAD_REQUEST)

```

PdcViewSetとActionViewSetは、ログインしているユーザーが作ったもののみ表示するようにしなければならないため、

```python
def get_queryset(self):
        return self.queryset.filter(ユーザー外部キー=self.request.user)
```
として、表示にfilterをかけている

###urls.pyを作成してそれぞれのパスを通してあげる

```python:pdca/urls.py
from django.urls import include, path
from rest_framework import routers
from .views import CategoryViewSet, PdcViewSet, ActionViewSet

router = routers.DefaultRouter()
router.register('category', CategoryViewSet)
router.register('pdc', PdcViewSet)
router.register('action', ActionViewSet)

urlpatterns = [
    path('', include(router.urls))
]
```

ViewSetを使ってviewを作成した場合は、routerを用いてpathを登録する。
genericsの場合はそのままurlpatternsに入れてしまう。

ここは、そういうルールと割り切って私は覚えてしまっている。

####最後にpdcaのAPI自体のパスを通してあげる

```python:workapp/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('admin/', admin.site.urls),
    path('auth_api/', include('auth_api.urls')),
    #今回追記したところ↓
    path('pdca/', include('pdca.urls')),

    path('authen/', include('djoser.urls.jwt')),
]

urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
```

ここまででこのアプリのサーバーサイド側の機能を全て開発することができました🎉🎉


##ここまでの感想

今回は、外部APIをまだ使っていないので、サーバーサイドの開発は、比較的難しくなかった。
今後、GoogleカレンダーAPIと連結させて更なる機能拡張を考えているので、ここから大変になるのではないかと思われる。。。

私みたいな、駆け出しのエンジニアにとっては
「<strong>とりあえず完成させる</strong>」をモットーに学習していった方が良いのではと思う。
成果物をとっとと仕上げてしまえば、それだけ自信にもなるし、モチベーションにも繋がる。

次回からReactを用いたフロント部分の開発の流れを発信していきます！！
今回のアプリ、フロントの実装がかなり手強かったです😅😅

最後に、ここまで読んでくださりありがとうございました🙇‍♂️🙇‍♂️

また、Twitterでも日々の積み上げや、プログラミング学習についてのツイートをしておりますので、よかったらフォローと応援の程よろしくお願いします！🙇‍♂️

Twitterアカウント↓
[健将@WEBエンジニア×明大生](https://twitter.com/kenshow_study)