こんにちは。sinyです。
この記事ではDjangoのテンプレートでデータベースに格納されている複数カラムの値を演算した結果をWEB画面に表示する方法についてご紹介します。
例えば、あるテーブルに「商品単価と数量」という2つのカラムが存在した場合に、「合計金額(=単価x数量)」を計算し商品毎の合計金額を一緒に画面表示したいようなケースがあると思います。
合計金額自体をテーブルのカラムとして定義してしまう方法もありますが、基本的にカラム値から計算できる合計金額をわざわざデータとして保持するのはあまりスマートとは言えないと思います。(パフォーマンスとの兼ね合いもありますが)
Djangoのテンプレートタグには様々な機能があるのですが、残念ながら掛け算を演算する機能が存在していないため、Djangoで自作フィルタを作成してDjangoのテンプレート内で合計金額を計算して表示するロジックを実装します。
事前設定
まずは、アプリケーションフォルダ直下にtemplatetagsという名前のフォルダを作成します。
その後、作成したtemplatetagsフォルダ内に__init__.pyと自作フィルタの処理内容を定義するpythonスクリプトファイル(今回はutils.py)を作成します。
自作テンプレートタグの設定
先ほど作成したutils.pyに以下のコードを記載します。
from django import template register = template.Library() @register.filter(name="multiply") def multipliy(value, args): return value * args
まず、自作フィルタを定義するためにtemplateをインポートしています。
以下の部分で自作フィルタの名称を定義します。
@register.filter(name="<自作フィルタの名称>")
最後に自作フィルタで実行される処理内容を関数として定義します。
def multipliy(value, args): return value * args
第一引数(value)がテンプレート内で受け取る変数で、第二引数(args)が掛ける数値になります。
今回のケースでは、valueに単価、argsに数量が入るイメージです。
テンプレートの設定
最後にテンプレートの設定です。
先ほど定義した自作フィルタをロードします。
{% load <templatetagsフォルダ内に作成したファイル名> %}
{% load utils %}
あとは、テンプレート内で以下の形式で記載すると、utils.py内で定義したmultiply関数がCALLされてvalueとargsの値を使って掛け算した結果がテンプレート内にReturnされます。
{{ value | multiply: args}}
以下は、金額、数量から合計金額を計算して表示するサンプルコードです。
- models.py
from django.db import models from datetime import datetime class Category(models.Model): class Meta: #カテゴリ verbose_name ="カテゴリ" verbose_name_plural ="カテゴリ" #カラム名の定義 category_name = models.CharField(max_length=255,unique=True) def __str__(self): return self.category_name class Kakeibo(models.Model): class Meta: verbose_name ="家計簿" verbose_name_plural ="家計簿" #カラムの定義 date = models.DateField("日付",default=datetime.now) category = models.ForeignKey(Category, on_delete = models.PROTECT, verbose_name="カテゴリ") money = models.IntegerField("金額", help_text="単位は日本円") quantity = models.IntegerField(verbose_name="数量",default=0) memo = models.CharField(verbose_name="メモ", max_length=500) def __str__(self): return self.memo
- views.py
#一覧表示用のDjango標準ビュー(ListView)を承継して一覧表示用のクラスを定義 class KakeiboListView(ListView): #利用するモデルを指定 model = Kakeibo #データを渡すテンプレートファイルを指定 template_name = 'kakeibo/kakeibo_list.html' #家計簿テーブルの全データを取得するメソッドを定義 def queryset(self): return Kakeibo.objects.all()
- テンプレートファイルの定義(kakeibo_list.html)
{% load utils %} {% block content %} ~省略~ <!-- テーブル表の定義 --> <table id=kakeibo_list width="100%" class="table table-striped table-bordered table-hover"> <!-- 表の列の定義--> <thead> <tr> <th class="text-center">日付</th> <th class="text-center">カテゴリ</th> <th class="text-center">金額</th> <th class="text-center">数量</th> <th class="text-center">合計金額</th> <th class="text-center">メモ</th> <th class="text-center">更新・削除</th> </tr> </thead> <!-- ここまでが表の列の定義--> <!-- 表のデータ部分の表示--> <tbody> {% for item in object_list %} <tr class="odd gradeX text-center"> <td class="text-center" width="100">{{ item.date}}</td> <td class="text-center" width="100">{{ item.category }}</td> <td class="text-center" width="140">{{ item.money }}</td> <td class="text-center" width="140">{{ item.quantity }}</td> <td class="text-center" width="140">{{ item.money | multiply:item.quantity}}</td> <td class="text-center" width="140">{{ item.memo }}</td> <td class="text-center" width="140"> <a class="btn btn-primary" href="{% url 'kakeibo:kakeibo_update' item.pk %}" role="button">更新ページ</a> <a class="btn btn-danger" href="{% url 'kakeibo:kakeibo_delete' item.pk %}" role="button">削除ページ</a> </td> </tr> {% endfor %} </tbody> <!-- ここまでが表のデータ部分の表示--> </table> <!-- ここまでがテーブル表の定義 --> ~省略~ {% endblock content %}
上記例では、以下の部分で自作のフィルタ(multiply)を利用して、Kakeiboテーブルクラスのmoney(金額)とquantipy(数量)同士を掛け算した結果を「合計金額」として表示しています。
{{ item.money | multiply:item.quantity}}
こんな感じで、金額x数量の結果(=合計金額)をレコード毎に表示することができます。
カスタムフィルタで複数の引数を受け取りたい場合
例えば、今回の例で消費税込みの合計金額(=money[金額]x 消費税率[1.08] x 数量[quantity])を表示したい場合やテーブルの3つ以上のカラム同士を掛け算したいような場合、カスタムフィルタで複数の引数を受け取るように以下のように設定してもエラーでうまく処理できません。
@register.filter(name="multiply") def multipliy(value, args1,args2): return int(value * args1 * args2)
どうやらテンプレート側からカスタムフィルタに渡す引数は文字列でないと処理が出来ないようです。
【解決策】
少し力技ですが、消費税込みの合計金額を表示したいケースで説明すると、以下のようにカスタムフィルタの関数を新たに追加することで実現できました。
from django import template register = template.Library() @register.filter(name="multiply") def multipliy(value, args): return int(value * args) def tax_included(var,args): return var * args register.filter('tax_included',tax_included)
「金額x税率」の計算結果を返すtax_included関数を定義しておきます。
テンプレート側では、以下の通りテンプレートタグのwithでtax_includedを使って一旦money(金額)x税率(1.08)を掛けた結果をtax_priceという変数名に格納しておきます。
その後、{{ tax_price | multiply:item.quantity }}の部分で税込み金額(tax_price)に対して数量(item.quantity)を掛けた値を戻り値として表示することで、税込みの合計金額を表示させることができます。
{% with item.money|tax_included:1.08 as tax_price %} <td class="text-center" width="140">{{ tax_price | multiply:item.quantity }}</td> {% endwith %}
以上、Djangoで自作フィルタを利用してテンプレート内で合計金額を表示するロジックの実装方法についての解説でした。