5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DjangoとVueでカンバンアプリケーションを作る(5)

Last updated at Posted at 2018-09-20

※はじめからはこちら

前回は作成済のボードの一覧画面を作りました。今回は、ボードの追加機能を実装します。

サーバサイドの実装

まずはボードを追加するAPIを作っていきます。

サービスレイヤーの実装

まずはボードを追加するメソッドを用意します。

application/modules/kanban/service.py
@@ -3,3 +3,16 @@ from .models import Board

 def get_board_list_by_owner(owner):
     return Board.get_list_by_owner(owner=owner)
+
+
+def add_board(owner, board_name):
+    """
+    :param User owner:
+    :param str board_name:
+    :return:
+    """
+    board = Board.objects.create(
+        owner=owner,
+        name=board_name
+    )
+    return board

add_boardに作成者とボード名を渡すことでBoardモデルを生成するようにします。

viewの実装

APIのためのViewを実装します。postで必要なパラメータを受け取ったらボードを生成するようにしますので、 BoardListApipostメソッドを追加します。

application/views/api/boards.py
@@ -1,3 +1,5 @@
+import json
+
 from django.http import JsonResponse
 from django.views.generic import View

class BoardListApi(View):

    def get(self, request):
        """
        ボードの一覧を戻す
        """
        board_list = []
        for board in kanban_sv.get_board_list_by_owner(request.user):
            board_list.append({
                'id': board.id,
                'name': board.name,
            })
         return JsonResponse({
             'board_list': board_list,
         })

+    def post(self, request):
+        """
+        新しいボードを追加する
+        """
+        data = json.loads(request.body)
+        board_name = data.get('boardName')
+        board = kanban_sv.add_board(
+            owner=request.user,
+            board_name=board_name
+        )
+        return JsonResponse({
+            'board_data': {
+                'id': board.id,
+                'name': board.name,
+            }
+        })

さて、これだけだと実行時にエラーが出ます。Djangoのデフォルトの設定ではCSRF対策が有効化されています。APIでCSRFを考慮するのは面倒なので、解除してしまいます。

application/views/api/boards.py
@@ -1,11 +1,14 @@
 import json

 from django.http import JsonResponse
+from django.views.decorators.csrf import csrf_exempt
+from django.utils.decorators import method_decorator
 from django.views.generic import View

 from modules.kanban import service as kanban_sv


+@method_decorator(csrf_exempt, name='dispatch')
 class BoardListApi(View):

     def get(self, request):

csrf_exemptというデコレータをつけると、そのViewについてはCSRF対策が無効化されます。Djangoのクラスベースビューを使ってる場合は、method_decoratorを使う必要があるので上のようなコードになります。厳密には、getpostに処理を引き渡すdispatchというメソッドがデフォルトでは定義されており、そこにcsrf_exemptを付与するというコードです。お作法のようなものです。

urls.pyに割当

今回は、urls.pyへの割当は不要です。前回定義した以下の割当があります。

application/views/urls.py
    path('api/boards/', login_required(BoardListApi.as_view())),

/api/boadsへのGETアクセスでボードの一覧を取得していました。今回は同じURLにPOSTアクセスしてボードを作るので、URL自体は増えないため、マッピングの追加は不要です。

クライアントサイドの実装

追加ボタンコンポーネントの作成

ボード一覧の最後に追加ボタンを用意したいと思います。デザインは揃えたいので、前回作成したBoardCard.vueをもとにAddBoardCard.vueを以下の様に作ります。

application/vuejs/src/pages/Home/components/AddBoardCard.vue
<template>
  <a href="#">
    <div class="card bg-dark text-white">
      <div class="card-body">
        <h5 class="card-title">Add Board(+)</h5>
      </div>
    </div>
  </a>
</template>

<script>
export default {
  name: 'AddBoardCard',
};
</script>

特に動きなどは何もまだ用意していないのでガワだけです。これをまずはHome画面に組み込みます。

application/vuejs/src/pages/Home/Index.vue
@@ -5,6 +5,7 @@
                  :title="board.name"
                  :boardId="board.id"
                  :key="board.id" />
+      <AddBoardCard class='board-card col-3' />
     </div>
   </div>
 </template>
@@ -12,6 +13,8 @@
 <script>
 import { createNamespacedHelpers } from 'vuex';
 import BoardCard from './components/BoardCard.vue';
+import AddBoardCard from './components/AddBoardCard.vue';
+

 const { mapState, mapActions } = createNamespacedHelpers('home');

@@ -19,6 +22,7 @@ export default {
   name: 'home',
   components: {
     BoardCard,
+    AddBoardCard,
   },
   computed: {
     ...mapState([

これで以下のように追加ボタンが表示されるようになりました。

KANBAN-3.png

KanbanClientへの追加

ボード追加のAPIを使えるようにするため、KanbanClientaddBoardメソッドを追加します。

application/vuejs/utils/kanbanClient.js
@@ -54,6 +54,11 @@ class KanbanClient extends Client {
     return response.data.boardList;
   }

+  async addBoard({ boardName }) {
+    await this._post(`${this.baseUrl}/boards/`, {
+      boardName,
+    });
+  }
 }

storeへの組み込み

StoreからaddBoardを使えるようにします。

application/vuejs/src/store/pages/home.js
@@ -10,6 +10,10 @@ const actions = {
     const boardList = await KanbanClient.getBoardList();
     commit('setBoardList', { boardList });
   },
+  async addBoard({ dispatch }, { boardName }) {
+    await KanbanClient.addBoard({ boardName });
+    dispatch('fetchBoardList');
+  },
 };

stateを更新せず、ボードの追加が完了したらdispatch('fetchBoardList');fetchBoardListアクションを呼び出し、ボード一覧の再取得をするようにしています。

コンポーネントへの組み込み

先程作成した、AddBoardCard.vueに組み込みます。

application/vuejs/src/pages/Home/components/AddBoardCard.vue
@@ -1,5 +1,5 @@
 <template>
-  <a href="#">
+  <a href="#" @click="addBoardAction">
     <div class="card bg-dark text-white">
       <div class="card-body">
         <h5 class="card-title">Add Board(+)</h5>
@@ -9,7 +9,23 @@
 </template>

 <script>
+import { createNamespacedHelpers } from 'vuex';
+
+const { mapActions } = createNamespacedHelpers('home');
+
+
 export default {
   name: 'AddBoardCard',
+  methods: {
+    addBoardAction() {
+      const boardName = window.prompt('BoardName?');
+      if (boardName) {
+        this.addBoard({
+          boardName,
+        });
+      }
+    },
+    ...mapActions(['addBoard']),
+  },
 };
 </script>

@click="addBoardAction"でこのコンポーネント自体がクリックされた際に、methodsに定義しているaddBoardActionを呼び出すようにしました。この中では、ボード名の入力を促し、受け付けた後は先程Storeに定義したaddBoardを呼び出しています。

これでボードの作成機能は実装完了です。以下のようにボードが追加できる様になっているはずです。

Image from Gyazo

次回

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?