【Django】テンプレートでカラム値同士の演算結果を表示|カスタムテンプレートタグ/フィルタ実装

スポンサードリンク



Django · Template Tags · カラム値の演算結果を表示

算結果を Django テンプレートで表示しようとすると、標準のテンプレート言語では四則演算が制限されています。「金額 × 数量で合計を表示」「在庫数を 100 で割って百分率にする」など、ちょっとした計算をテンプレートで実現するには、カスタムテンプレートタグ or テンプレートフィルタ を作るのが Django 流のやり方。本記事では、モデルのカラム値同士で演算した結果をテンプレートに表示する実装手順を解説します。

この記事ではDjangoのテンプレートでデータベースに格納されている複数カラムの値を演算した結果をWEB画面に表示する方法についてご紹介します。

例えば、あるテーブルに「商品単価と数量」という2つのカラムが存在した場合に、「合計金額(=単価x数量)」を計算し商品毎の合計金額を一緒に画面表示したいようなケースがあると思います。

合計金額自体をテーブルのカラムとして定義してしまう方法もありますが、基本的にカラム値から計算できる合計金額をわざわざデータとして保持するのはあまりスマートとは言えないと思います。(パフォーマンスとの兼ね合いもありますが)

Djangoのテンプレートタグには様々な機能があるのですが、残念ながら掛け算を演算する機能が存在していないため、Djangoで自作フィルタを作成してDjangoのテンプレート内で合計金額を計算して表示するロジックを実装します。

user@sinyblog:~/article 01_section_1.md事前設定

まずは、アプリケーションフォルダ直下にtemplatetagsという名前のフォルダを作成します。

その後、作成したtemplatetagsフォルダ内に__init__.pyと自作フィルタの処理内容を定義するpythonスクリプトファイル(今回はutils.py)を作成します。

user@sinyblog:~/article 02_section_2.md自作テンプレートタグの設定

先ほど作成したutils.pyに以下のコードを記載します。

python


from django import template

 

 

register = template.Library()

 

@register.filter(name="multiply")

def multipliy(value, args):

    return value * args

 

まず、自作フィルタを定義するためにtemplateをインポートしています。

以下の部分で自作フィルタの名称を定義します。

@register.filter(name="<自作フィルタの名称>")

最後に自作フィルタで実行される処理内容を関数として定義します。

python


def multipliy(value, args):

    return value * args

 

第一引数(value)がテンプレート内で受け取る変数で、第二引数(args)が掛ける数値になります。

今回のケースでは、valueに単価、argsに数量が入るイメージです。

user@sinyblog:~/article 03_section_3.mdテンプレートの設定

最後にテンプレートの設定です。

先ほど定義した自作フィルタをロードします。

{% load <templatetagsフォルダ内に作成したファイル名> %}

python


{% load utils %}

あとは、テンプレート内で以下の形式で記載すると、utils.py内で定義したmultiply関数がCALLされてvalueargsの値を使って掛け算した結果がテンプレート内にReturnされます。

{{ value | multiply: args}}

以下は、金額、数量から合計金額を計算して表示するサンプルコードです。

  • models.py
python


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
python


#一覧表示用のDjango標準ビュー(ListView)を承継して一覧表示用のクラスを定義

class KakeiboListView(ListView):

   #利用するモデルを指定

   model = Kakeibo

   #データを渡すテンプレートファイルを指定

   template_name = 'kakeibo/kakeibo_list.html'



   #家計簿テーブルの全データを取得するメソッドを定義

   def queryset(self):

       return Kakeibo.objects.all()

 

  • テンプレートファイルの定義(kakeibo_list.html)
python


 {% 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(数量)同士を掛け算した結果を「合計金額」として表示しています。

python


{{ item.money | multiply:item.quantity}}

 

こんな感じで、金額x数量の結果(=合計金額)をレコード毎に表示することができます。

user@sinyblog:~/article 04_section_4.mdカスタムフィルタで複数の引数を受け取りたい場合

例えば、今回の例で消費税込みの合計金額=money[金額]x 消費税率[1.08] x 数量[quantity])を表示したい場合やテーブルの3つ以上のカラム同士を掛け算したいような場合、カスタムフィルタで複数の引数を受け取るように以下のように設定してもエラーでうまく処理できません。

python


@register.filter(name="multiply")

def multipliy(value, args1,args2):

    return int(value * args1 * args2)

どうやらテンプレート側からカスタムフィルタに渡す引数は文字列でないと処理が出来ないようです。

【解決策】

少し力技ですが、消費税込みの合計金額を表示したいケースで説明すると、以下のようにカスタムフィルタの関数を新たに追加することで実現できました。

python


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関数を定義しておきます。

テンプレート側では、以下の通りテンプレートタグのwithtax_includedを使って一旦money(金額)x税率(1.08)を掛けた結果をtax_priceという変数名に格納しておきます。

その後、{{ tax_price | multiply:item.quantity }}の部分で税込み金額(tax_price)に対して数量(item.quantity)を掛けた値を戻り値として表示することで、税込みの合計金額を表示させることができます。

python


{% with item.money|tax_included:1.08 as tax_price %}

     <td class="text-center" width="140">{{ tax_price | multiply:item.quantity }}</td>

 {% endwith %}

以上、Djangoで自作フィルタを利用してテンプレート内で合計金額を表示するロジックの実装方法についての解説でした。

 

おすすめの記事