LoginSignup
3
2

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-10-28

※はじめからはこちら

前回でカードの並び替えはできるようになりました。今回はカードの追加を実装していきます。

カードの追加

カードを追加できるようにしていきます。すでにボタン自体は追加済です。

KANBAN-8.png

これを押した時のロジックを実装します。ポイントはこのロジックはWebsocket経由ではなく、RESTのようにAjaxで実装したほうが望ましいというのがポイントです。

Card追加のサービス実装

まずはサーバサイド側でCardの追加ロジックを実装します。カードはそのPipeLineの末尾に追加します。CardはPipeLine内での位置をorderという属性で管理していますので、追加時はその最大のorderよりも大きい値をセットします。orderは連番であることを期待しているので、単にCardの数+1で良いはずです。

まずは、そのPipeLine内のCard数を戻すクエリを追加します。

application/modules/kanban/models/card.py
@@ -23,3 +23,7 @@ class Card(models.Model):
             return cls.objects.get(id=card_id)
         except cls.DoesNotExist:
             return None
+
+    @classmethod
+    def get_current_card_count_by_pipe_line(cls, pipe_line):
+        return cls.objects.filter(pipe_line=pipe_line).count()

そして、これを利用したadd_cardメソッドを追加します。

application/modules/kanban/service.py
@@ -63,3 +63,14 @@ def update_card_order(pipe_line_id, card_id_list):
         card.order = i
         card.pipe_line = pipe_line
         card.save()
+
+
+def add_card(pipe_line_id, card_title):
+    pipe_line = PipeLine.get_by_id(pipe_line_id)
+    current_count = Card.get_current_card_count_by_pipe_line(pipe_line)
+    card = Card(
+        title=card_title,
+        content=None,
+        pipe_line=pipe_line,
+        order=current_count + 1,  # 現在のカード数 + 1で末尾になるはず
+    )
+    card.save()
+    return card

add_card用のAPIを実装

add_cardを呼び出すAPIとしてCardApiを追加します。

application/views/api/boards.py
@@ -40,4 +40,27 @@ class BoardListApi(View):
                 'id': board.id,
                 'name': board.name,
             }
+
+
+@method_decorator(csrf_exempt, name='dispatch')
+class CardApi(View):
+
+    def post(self, request):
+        """
+        新しいcardを追加する
+        """
+        data = json.loads(request.body)
+        card_title = data.get('cardTitle')
+        pipe_line_id = data.get('pipeLineId')
+
+        card = kanban_sv.add_card(
+            pipe_line_id=pipe_line_id,
+            card_title=card_title
+        )
+        return JsonResponse({
+            'card_data': {
+                'id': card.id,
+                'name': card.title,
+            }
+        })

cardTitlepipeLineIdをパラメータとしてとるようにします。続いて、これをURLに割り当てます。

application/views/urls.py
@@ -7,7 +7,7 @@ from django.views.generic import TemplateView

 from .accounts import signup as signup_view
 from .api.accounts import AccountsApi
-from .api.boards import BoardListApi
+from .api.boards import BoardListApi, CardApi


 urlpatterns = [
@@ -17,6 +17,7 @@ urlpatterns = [

     path('api/accounts/', AccountsApi.as_view()),
     path('api/boards/', login_required(BoardListApi.as_view())),
+    path('api/cards/', login_required(CardApi.as_view())),
 ]

これでapi/cards/にPOSTすればCardが作成されるはずです。

KanbanClientへの追加

フロント側からサーバサイドへアクセスする際に利用するAPIClient(KanbanClient)にapi/cards用のメソッドを追加します。

application/vuejs/utils/kanbanClient.js
@@ -59,6 +59,13 @@ class KanbanClient extends Client {
       boardName,
     });
   }
+
+  async addCard({ cardTitle, pipeLineId }) {
+    await this._post(`${this.baseUrl}/boards/`, {
+      cardTitle,
+      pipeLineId,
+    });
+  }
 }

Store/Componentへの組み込み

KanbanClientを利用してAPIへアクセスするように組み込んでいきます。まずはStoreのActionを追加します。

application/vuejs/src/store/pages/board.js
@@ -1,4 +1,5 @@
 import camelcaseKeys from 'camelcase-keys';
+import kanbanClient from "../../../utils/kanbanClient";


 const state = {
@@ -31,6 +32,12 @@ const actions = {
     });
     commit('updateCardOrder', { pipeLineId, cardList });
   },
+  async addCard({ commit }, { pipeLineId, cardTitle }) {
+    await kanbanClient.addCard({
+      pipeLineId,
+      cardTitle,
+    });
+  },
 };

続いて、このaddBoardPipeLine.vueに組み込みます。

application/vuejs/src/pages/Board/components/BoardArea/PipeLine.vue
@@ -75,12 +75,19 @@ export default {
   methods: {
     addCardAction() {
       const cardTitle = window.prompt('CardTitle?');
+      if (cardTitle) {
+        this.addCard({
+          pipeLineId: this.pipeLine.pipeLineId,
+          cardTitle,
+        });
+      }
     },
     delPipeLineAction() {
       window.confirm(`DELETE [${this.pipeLineName}] ? Are you sure?`);
     },
     ...mapActions([
       'updateCardOrder',
+      'addCard',
     ]),
     ...mapGetters([
       'getBoardId',

これでカード追加ボタンを押すと、カードが追加されるようになりますが、追加されたカードはページをリロードするまで見えません。

これは、カードが追加されたあとにボード全体のデータの再取得が行われていないためです。また、これは追加を行ったブラウザ以外に対してもデータの再取得をリクエストする必要もあります。

Card追加完了後のデータ再取得

カード追加完了後は、自身を含む全てのクライアントが新しいデータを取得する必要があります。これはちょうどカードの並び替えで行っている以下の部分と同じことです。

application/views/ws/kanban_consumer.py
    def update_card_order(self, content):
        :
        async_to_sync(self.channel_layer.group_send)(
            self.room_group_name,
            {
                'type': 'send_board_data',
            }
        )

このChannelLayerを利用している処理単体をWebsocketを通じてクライアントが呼び出せるようにします。

application/views/ws/kanban_consumer.py
@@ -15,7 +15,8 @@ class KanbanConsumer(JsonWebsocketConsumer):
         self.board_id = None
         self.namespace = 'board'
         self.action_map = {
-            'update_card_order': self.update_card_order
+            'update_card_order': self.update_card_order,
+            'broadcast_board_data': self.broadcast_board_data
         }
         self.room_group_name = None

@@ -80,3 +81,16 @@ class KanbanConsumer(JsonWebsocketConsumer):
         if not action:
             raise Exception('{} is not a valid action_type'.format(content['type']))
         action(content)
+
+    def broadcast_board_data(self, content=None):
+        """
+        全クライアントに、ボードデータの再取得を依頼する
+        :param content:
+        :return:
+        """
+        async_to_sync(self.channel_layer.group_send)(
+            self.room_group_name,
+            {
+                'type': 'send_board_data',
+            }
+        )

これで、broadcast_board_dataというメッセージをサーバ側に送信することで同じボードを開いている全クライアントのデータが更新できるようになります。

これでカードの追加処理は実装完了です。

次回

カードの中身の変更とかを作っていきます。

3
2
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
3
2