【Django入門27】カスタムテンプレートフィルタの作成|独自のテンプレートタグを追加する方法

テンプレートフィルタとは?

テンプレートフィルタの概要

Djangoのテンプレートフィルタとは、テンプレート内で表示されるデータに対して、フォーマットの変更や特定の処理を適用するための機能です。たとえば、日付形式の変換、文字列のトリム、数値のフォーマットなどがよく使われます。

Djangoには、datelowerdefaultなど多くの組み込みフィルタがありますが、プロジェクトによっては独自のフィルタが必要になることがあります。そのような場合に、カスタムテンプレートフィルタを作成します。

組み込みテンプレートフィルタの例

Djangoが提供する組み込みフィルタの一例を以下に示します:

例1:文字列を小文字に変換する

{{ "HELLO WORLD"|lower }}

出力:hello world

例2:日付形式の変更

{{ post.published_at|date:"Y年m月d日" }}

出力:2023年02月15日(例)

カスタムテンプレートフィルタの必要性

なぜカスタムフィルタが必要なのか?

  • Djangoの組み込みフィルタでは対応できない独自の処理を行いたい場合。
  • 特定のビジネスルールに基づいてデータを変換する必要がある場合。
  • テンプレート内のコードをシンプルにし、再利用性を高めたい場合。

カスタムテンプレートフィルタの作成手順

1. カスタムフィルタ用のアプリケーションディレクトリを作成

テンプレートフィルタを格納するために、Djangoプロジェクト内のアプリケーションディレクトリにファイルを作成します。

ディレクトリ構成の例:

myproject/
    ├── myapp/
    │   ├── __init__.py
    │   ├── models.py
    │   ├── views.py
    │   ├── templates/
    │   ├── templatetags/
    │       ├── __init__.py
    │       └── custom_filters.py

2. カスタムフィルタの定義

templatetags/custom_filters.pyファイル内にフィルタのロジックを記述します。

custom_filters.py:

from django import template

register = template.Library()

@register.filter(name='truncate_chars')
def truncate_chars(value, num):
    """
    文字列を指定された文字数に切り詰める。
    """
    if len(value) > num:
        return value[:num] + '...'
    return value
  • @register.filter:フィルタを登録するためのデコレーター。
  • value:フィルタが適用される値。
  • num:フィルタに渡される引数。

3. テンプレートでのフィルタの使用

カスタムフィルタを使用するテンプレート内で、{% load %}を使ってフィルタを読み込みます。

templates/example.html:

{% load custom_filters %}

<p>{{ article.content|truncate_chars:50 }}</p>

この例では、記事の内容を50文字に切り詰め、必要であれば末尾に...を追加して表示します。

引数を持つカスタムフィルタの作成

文字列内の特定の単語を置換するフィルタ

custom_filters.py:

@register.filter(name='replace_word')
def replace_word(value, args):
    """
    文字列内の特定の単語を別の単語に置き換える。
    argsは '元の単語,置換する単語' の形式で渡す。
    """
    try:
        old_word, new_word = args.split(',')
        return value.replace(old_word, new_word)
    except ValueError:
        return value  # 引数が正しくない場合は元の値を返す

templates/example.html:

{% load custom_filters %}

<p>{{ article.content|replace_word:"Django,Python" }}</p>
  • この例では、article.content内の「Django」をすべて「Python」に置き換えます。

フィルタのテスト

テスト環境の設定

カスタムフィルタが正しく動作することを確認するために、Djangoのユニットテスト機能を使用します。

tests.py:

from django.test import TestCase
from .templatetags.custom_filters import truncate_chars, replace_word

class CustomFilterTests(TestCase):

    def test_truncate_chars(self):
        result = truncate_chars("これは長いテキストです", 5)
        self.assertEqual(result, "これは長...")

    def test_replace_word(self):
        result = replace_word("Django is great", "Django,Python")
        self.assertEqual(result, "Python is great")
  • assertEqual: フィルタの出力が期待値と一致するかを確認します。

テストの実行

python manage.py test

実際のプロジェクトでの応用例

1. カスタム日付フォーマット

特定の形式で日付を表示するカスタムフィルタを作成し、ビジネス要件に応じた表示が可能です。

custom_filters.py:

from datetime import datetime

@register.filter(name='custom_date_format')
def custom_date_format(value, format_str="%Y年%m月%d日"):
    """
    日付を指定されたフォーマットで表示する。
    """
    if isinstance(value, datetime):
        return value.strftime(format_str)
    return value

2. 敏感情報のマスキング

たとえば、クレジットカード番号の末尾4桁だけを表示するフィルタなど、セキュリティ関連の用途でも使用できます。

custom_filters.py:

@register.filter(name='mask_sensitive')
def mask_sensitive(value, visible_digits=4):
    """
    文字列の先頭部分をマスクし、末尾の桁数だけ表示する。
    """
    masked_length = len(value) - visible_digits
    return '*' * masked_length + value[-visible_digits:]

templates/example.html:

<p>カード番号: {{ "1234567812345678"|mask_sensitive }}</p>

出力:************5678

まとめ

Djangoのカスタムテンプレートフィルタは、プロジェクト固有の要件に対応するための柔軟なツールです。フィルタを適切に活用することで、テンプレート内のコードが簡潔になり、再利用可能なコンポーネントとして設計できます。