D3.js + django -- 目標に費やした時間(累積)を折れ線グラフで描画する(1)

概要

  • ある目標に対して、どれだけの時間を費やしているのか知りたい
  • 新しく言語を学ぶ場合などで、学習に必要な時間の目安を得られる(かもしれない)
  • 今回は Django でのモデル定義 → (データの蓄積) → JSON でレスポンスを返す、部分まで

モデルを定義する

  • 複数の目標が、同じカテゴリーに属する (カテゴリー 1 : 目標 N)
  • 一つの目標に対して、努力を(複数回)重ねる (目標 1 : 努力 : N)

以上を考慮して、以下のようなモデルを考える (※コードは必要最小限の部分のみ)。
f:id:zdassen:20180219223408j:plain
app_name/models.py

# app_name/models.py
from django.contrib.auth.models import User


class Category(models.Model):
    """カテゴリー"""

    # ユーザー
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        verbose_name="ユーザー"
    )

    # カテゴリー名
    name = models.CharField(
        max_length=64,
        verbose_name="カテゴリー名"
    )

    def __str__(self):
        return "%s (%s)" % (
            self.name,
            self.user.get_username(),
        )


class Target(models.Model):
    """目標"""

    # ユーザー
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        verbose_name="ユーザー"
    )

    # カテゴリー
    category = models.ForeignKey(
        Category,
        on_delete=models.CASCADE,
        verbose_name="カテゴリー"
    )

    # 目標達成のイメージ
    description = models.CharField(
        max_length=64,
        verbose_name="目標"
    )

    def __str__(self):
        return "[%s] %s" % (
            self.category.name,
            self.description,
        )


class Effort(models.Model):
    """目標に対する努力"""

    # ユーザー
    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        verbose_name="ユーザー"
    )

    # 目標(何に対しての努力か)
    target = models.ForeignKey(
        Target,
        on_delete=models.CASCADE,
        verbose_name="目標"
    )

    # 日時(いつ努力したか)
    at = models.DateTimeField(
        verbose_name="日時"
    )

    # 時間(目標に対して何分努力したか)
    cost = models.PositiveSmallIntegerField(
        null=False,
        blank=False,
        verbose_name="コスト(分)"
    )

    def __str__(self):
        return "%s %s ~ %smin" % (
            self.target,
            self.at,
            self.cost,
        )

Effort (努力の実績) に関するデータを JSON で取得する

/app_name/effort-list-json/ を定義して、以下のメソッドと紐づける。

# app_name/views.py
from collections import defaultdict
from django.http import JsonResponse


def effort_list_json(request):
    """努力実績(JSONデータ)"""

    # ログイン中のユーザーについての実績を取得する
    effort_list = Effort.objects.filter(
        user=request.user
    )

    # 「カテゴリー名 + 目標」 で整理する
    d = defaultdict(list)
    for effort in effort_list:
        target = "[%s] %s" % (    # [カテゴリー名] 目標
            effort.target.category.name,
            effort.target.description,
        )
        d[target].append({
            "at": str(effort.at),    # 努力をした日時
            "cost": effort.cost,    # 努力した時間(分)
        })

    response = JsonResponse(d)

    return response