自動化無しに生活無し

WEBとかAIとかLinux関係をひたすら書く備忘録系ブログ

【Django】モデルに計算可能な時間を記録する【勉強時間・筋トレ時間の記録系ウェブアプリの作成に】【DurationField】

thumbnail

例えば、勉強時間や筋トレ時間を記録するウェブアプリを作るとする。

この時に、ネックになるのが、時間を記録するモデルフィールド。

IntegerFieldで記録するべきか、DatetimeFieldでtimedeltaを使うか。

いずれにせよ、合計や平均などを出さないといけないので、このフィールド選択を間違えると後々大変なことになる。

フォームの形式も考慮する必要がある。

そこで、本記事では、時間を記録する方法の最適解に近づけるよう考察する。

方法論と問題点

方法論 問題点
開始時刻と終了時刻をDateTimeFieldで記録する 途中休憩を挟むことができない、休憩を挟むたびにレコードが入る
実行時間をIntegerFieldで記録する 時間の計算をする必要がある。単位をミリ秒とするか、秒とするかで揉める
timedelta型のDurationFieldを使う 計算は簡単だが、テンプレートの表示にも問題あり

DurationFieldとは?

秒単位でもフォーマット(時間:分:秒)でも指定可能なフィールド。datetime.timedelta型に変換してくれる。

DurationFieldはtimedelta型なので、計算処理は楽だ(平均、合計も簡単に出せる)。

だが、テンプレートの表示に問題がある。

これさえどうにかなれば、DurationFieldを採用すると言う選択肢はアリだと思う。Stackoverflowで検索したところ、どうやらカスタムテンプレートタグしか選択肢はないようだ。

https://stackoverflow.com/questions/33105457/display-and-format-django-durationfield-in-template

上記サイトのカスタムテンプレートタグを使うことで解決できる。

モデルメソッドを使って表記を変える。

モデルメソッドを使って表現することもできる。カスタムテンプレートタグを使いたくない場合はこちらが良いだろう。

from django.db import models

class Topic(models.Model):

    comment     = models.CharField(verbose_name="コメント",max_length=2000)
    time        = models.DurationField(verbose_name="トレーニング時間")

    #DurationFieldの表示
    def time_format(self):
        total   = int(self.time.total_seconds())

        hours   = total // 3600
        minutes = (total % 3600) // 60
        seconds = (total % 60)

        return '{}時間{}分{}秒'.format(hours, minutes, seconds)

ビューにて.aggregate(Sum("time"))を使う。

from django.shortcuts import render,redirect

from django.views import View
from django.db.models import Sum

from .models import Topic

class IndexView(View):

    def get(self, request, *args, **kwargs):

        context             = {}
        context["topics"]   = Topic.objects.all()

        #集計とフォーマット作成
        topics  = Topic.objects.all().aggregate(Sum("time"))
        
        if topics["time__sum"]:

            total   = int(topics["time__sum"].total_seconds())
    
            hours   = total // 3600
            minutes = (total % 3600) // 60
            seconds = (total % 60) 
    
            context["total"]    = "{}時間{}分{}秒".format(hours, minutes, seconds)

        return render(request,"bbs/index.html",context)

    def post(self, request, *args, **kwargs):

        posted  = Topic( comment = request.POST["comment"] )
        posted.save()

        return redirect("bbs:index")

index   = IndexView.as_view()

結論

カスタムテンプレートタグが使える環境下であれば、DurationFieldを採用すると良いだろう。

カスタムテンプレートタグが使えない環境下であれば、IntegerFieldかDateTimeFieldしか選択肢はない。

と思っていたが、カスタムテンプレートタグはなくても表現する方法はいくらでもあるようだ。

考慮した結果、ミリ秒単位以下で記録をしたい場合はIntegerField()

秒単位で記録をしたい場合はDurationField()という結論が得られた。

スポンサーリンク

シェアボタン

Twitter LINEで送る Facebook はてなブログ