5
7

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でカンバンアプリケーションを作る(7)

Last updated at Posted at 2018-09-23

※はじめからはこちら

さて、前回はConsumerの大まかな部分をつくりましたので実際の処理をConsumerにいれていき、カンバンが表示できるようにしていきます。

Consumerへの処理の組み込み

Consumerにボード全体のデータを戻すメソッドを実装していきます。

ボードに必要なモデルの追加

今はBoardモデルだけが実装されていますが、ボードを表現するにはPipeLineCardのモデルが足りないので先に作ります。

KANBAN_🔊.png

application/modules/kanban/models/pipe_line.py
from django.db import models


class PipeLine(models.Model):

    board = models.ForeignKey('kanban.Board', on_delete=models.CASCADE)
    name = models.CharField(max_length=255)
    order = models.IntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return '{}: {} of {}'.format(self.pk, self.name, self.board)

application/modules/kanban/models/pipe_line.py
from django.db import models


class Card(models.Model):

    pipe_line = models.ForeignKey('kanban.PipeLine', on_delete=models.CASCADE)
    title = models.CharField(max_length=255)
    content = models.TextField(null=True, blank=True)
    order = models.IntegerField(default=0)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return '{}: {} of {}'.format(self.pk, self.title, self.pipe_line)

CardもPipeLineも並び替えができるように表示順を管理するためのorderという属性をもたせておきます。さらに、__init__.pyに追記します。

application/modules/kanban/models/__init__.py
@@ -1 +1,3 @@
 from .board import Board
+from .pipe_line import PipeLine
+from .card import Card

では、マイグレーションを行います。

$ docker-compose exec service python manage.py makemigrations kanban
$ docker-compose exec service python manage.py migrate kanban
実行例
$ docker-compose exec service python manage.py makemigrations kanban
WARNING: The DJANGO_ENV variable is not set. Defaulting to a blank string.
Migrations for 'kanban':
  application/modules/kanban/migrations/0002_auto_20180923_0436.py
    - Create model Card
    - Create model PipeLine
    - Add field pipe_line to card
$ docker-compose exec service python manage.py migrate kanban
WARNING: The DJANGO_ENV variable is not set. Defaulting to a blank string.
Operations to perform:
  Apply all migrations: kanban
Running migrations:
  Applying kanban.0002_auto_20180923_0436... OK

これでモデルができました。ダミーデータを作りたいのでDjango Afminに登録もしておきます。

application/modules/kanban/admin.py
@@ -1,7 +1,7 @@
 from django.contrib import admin

-from .models import Board
+from .models import Board, PipeLine, Card

 admin.site.register(Board)
+admin.site.register(PipeLine)
+admin.site.register(Card)

http://localhost:3000/admin/ にアクセスすれば、以下のようにPipeLineとCardがメニューに追加されているはずです。

Site_administration___Django_site_admin-3.png

次の作業のためにダミーデータとしてPipeLineを1つ、Cardを2つ作っておきましょう。

  • PipeLine追加
Add_pipe_line___Django_site_admin.png
  • Card追加
Add_card___Django_site_admin.png Add_card___Django_site_admin-2.png

ボード全体のデータを戻すメソッドの実装

続いてボードの構成情報をJSONで戻すメソッドを実装します。最終的に以下のようなデータを戻すようにします。

最終的なデータ形式
{
    'board_id': 1,
    'name': 'board name',
    'pipe_line_list': [
        {
            'pipe_line_id': 1,
            'name': 'PipeLine Name1',
            'card_list': [
                {
                    'card_id': 1,
                    'title': 'card1 title',
                    'content': 'content of card1',
                },
                {
                    'card_id': 2,
                    'title': 'card2 title',
                    'content': 'content of card2',
                },
            ],
        },
    ],
}

基本的な流れとしては、あるBoardを取得し、そのボードに紐づくPipeLineの一覧を取得し、各PipeLineに紐づくCardの一覧を取得していく形になります。そのため必要なデータを取得するためのメソッドを各モデルに追加していきます。

まずは、ID指定でBoardが取得できるようにするget_by_idメソッドを追加します。

application/modules/kanban/models/board.py
@@ -15,3 +15,10 @@ class Board(models.Model):
     @classmethod
     def get_list_by_owner(cls, owner):
         return list(cls.objects.filter(owner=owner).order_by('updated_at'))
+
+    @classmethod
+    def get_by_id(cls, board_id):
+        try:
+            return cls.objects.get(pk=board_id)
+        except cls.DoesNotExist:
+            return None

続いて、PipeLineに、あるBoardに紐づくものすべてのPipeLineを戻すメソッド(get_list_by_board)を追加します。並び替えに対応するため、order_by('order')も付与しておきます。

application/modules/kanban/models/pipe_line.py
@@ -11,3 +11,7 @@ class PipeLine(models.Model):

     def __str__(self):
         return '{}: {} of {}'.format(self.pk, self.name, self.board)
+
+    @classmethod
+    def get_list_by_board(cls, board):
+        return list(cls.objects.filter(board=board).order_by('order'))

最後はCardに、特定のPipeLineに紐づくCardを一括で戻すメソッドを追加します。同じ様にorder_by('order')も付与します。

application/modules/kanban/models/card.py
@@ -12,3 +12,7 @@ class Card(models.Model):

     def __str__(self):
         return '{}: {} of {}'.format(self.pk, self.title, self.pipe_line)
+
+    @classmethod
+    def get_list_by_pipe_line(cls, pipe_line):
+        return list(cls.objects.filter(pipe_line=pipe_line).order_by('order'))

これでモデルの部分は用意できたので、サービスメソッドを実装します。

application/modules/kanban/service.py
@@ -1,4 +1,4 @@
-from .models import Board
+from .models import Board, Card, PipeLine


 def get_board_list_by_owner(owner):
@@ -16,3 +16,36 @@ def add_board(owner, board_name):
         name=board_name
     )
     return board
+
+
+def get_board_data_by_board_id(board_id):
+    """
+    borad and pipeline and card.
+    :param int board_id:
+    :return:
+    :rtype: dict
+    """
+    # ボード取得
+    board = Board.get_by_id(board_id)
+    board_data = {
+        'board_id': board.id,
+        'name': board.name,
+        'pipe_line_list': []
+    }
+    # ボードに紐づく各パイプライン取得
+    for pipe_line in PipeLine.get_list_by_board(board):
+        pipe_line_data = {
+            'pipe_line_id': pipe_line.id,
+            'name': pipe_line.name,
+            'card_list': []
+        }
+        # パイプラインに紐づくカードを取得
+        for card in Card.get_list_by_pipe_line(pipe_line):
+            pipe_line_data['card_list'].append({
+                'card_id': card.id,
+                'title': card.title,
+                'content': card.content,
+            })
+        board_data['pipe_line_list'].append(pipe_line_data)
+
+    return board_data

簡単に動作確認をしておきます。Djangoではmanage.py shellで実装したメソッドなどが試せるので以下の様にget_board_data_by_board_idを実行してみます。

動作確認
$ docker-compose exec service ./manage.py shell  # docker内でmanage.py shellを起動
>>> from pprint import pprint
>>> from modules.kanban.service import get_board_data_by_board_id
>>> pprint(get_board_data_by_board_id(1))
{'board_id': 1,
 'name': 'MyBoard1',
 'pipe_line_list': [{'card_list': [{'card_id': 1,
                                    'content': 'content of Card1',
                                    'title': 'Card1'},
                                   {'card_id': 2,
                                    'content': 'content of card2',
                                    'title': 'Card2'}],
                     'name': 'MyList1',
                     'pipe_line_id': 1}]}

データの中身などは少し違うかもしれませんが、大体このような形式になっていれば大丈夫です。

Consumerへの組み込み

WebSocektでConsumerに接続した際にボードデータを初期値として戻すように組み込んでいきましょう。

application/views/ws/kanban_consumer.py
@@ -1,5 +1,7 @@
 from channels.generic.websocket import JsonWebsocketConsumer

+from modules.kanban import service as kanban_sv
+

 class KanbanConsumer(JsonWebsocketConsumer):

@@ -9,14 +11,25 @@ class KanbanConsumer(JsonWebsocketConsumer):
         self.consumer_id = id(self)
         # 後で使う
         self.user = None
+        self.board_id = None

     def connect(self):
         # 認証チェック
         if not self.scope['user'].is_authenticated:
             self.close()
             return

         self.user = self.scope['user']
+        self.board_id = self.scope['url_route']['kwargs']['board_id']
         # 接続を受け入れる
         self.accept()
+        # 初期データをクライアントに返送
+        self.send_board_data()
+
+    def send_board_data(self):
+        board_data = kanban_sv.get_board_data_by_board_id(self.board_id)
+        self.send_json({
+            'boardData': board_data
+        })
+        print(board_data)
+

self.scope['url_route']['kwargs']['board_id']はURLのマッピングルールで'ws/boards/<int:board_id>'boad_idにマッチした情報を取得するためのものです。この箇所でURLに含まれているBoardのIDを取得しています。

さらに、先程作ったget_board_data_by_board_idを利用して取得したボードの構成情報をクライアントに戻すsend_board_dataメソッドを定義し、connect内で呼び出すようにしています。

動作チェック

前回作成したwstest.pyを少し書き換えて使います。

wstest.py
from wsrequests import WsRequests

wsr = WsRequests()

# login django
wsr.get('http://localhost:3000/accounts/login/')
wsr.post(
    'http://localhost:3000/accounts/login/',
    data={
        'username': 'test',  # Djangoのユーザ名とパスワード
        'password': 'test',
        'csrfmiddlewaretoken': wsr.cookies['csrftoken'],
        'next': '/',
    }
)
wsr.connect('ws://localhost:3000/ws/boards/1')
+ wsr.receive_message()
wsr.disconnect()

接続時に返送されるメッセージを受け取るコードに書き換えました。実行して以下の2行目のようにボードの構成データが戻れば成功です。

$ python wstest.py
2018-09-23 21:16:53,874 - DEBUG - MainThread - connect websocket [ws://localhost:3000/ws/boards/1]
2018-09-23 21:16:53,904 - DEBUG - MainThread - receive message [{"boardData": {"board_id": 1, "name": "MyBoard1", "pipe_line_list": [{"pipe_line_id": 1, "name": "MyList1", "card_list": [{"card_id": 1, "title": "Card1", "content": "content of Card1"}, {"card_id": 2, "title": "Card2", "content": "content of card2"}]}]}}]
2018-09-23 21:16:53,905 - DEBUG - MainThread - disconnect websocket [ws://localhost:3000/ws/boards/1]

次回

5
7
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
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?