※はじめからはこちら
さて、前回はConsumerの大まかな部分をつくりましたので実際の処理をConsumerにいれていき、カンバンが表示できるようにしていきます。
Consumerへの処理の組み込み
Consumerにボード全体のデータを戻すメソッドを実装していきます。
ボードに必要なモデルの追加
今はBoard
モデルだけが実装されていますが、ボードを表現するにはPipeLine
とCard
のモデルが足りないので先に作ります。
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)
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
に追記します。
@@ -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に登録もしておきます。
@@ -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がメニューに追加されているはずです。

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

- Card追加


ボード全体のデータを戻すメソッドの実装
続いてボードの構成情報を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
メソッドを追加します。
@@ -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')
も付与しておきます。
@@ -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')
も付与します。
@@ -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'))
これでモデルの部分は用意できたので、サービスメソッドを実装します。
@@ -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に接続した際にボードデータを初期値として戻すように組み込んでいきましょう。
@@ -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
を少し書き換えて使います。
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]
次回