[Dd]enzow(ill)? with DB and Python

DBとか資格とかPythonとかの話をつらつらと

Djangoのtemplateでifを省略する(yesnoフィルター)

Djangoのtemplateでちょっとしたifを省略する場合の話です。Listを元にDOMノードを作成しているときに、元データに応じてCSSクラスを変更したい場合の話です。以下のようなケースをもう少しきれいに書きたくなったので調べました。

    <ul>
        {% for row in data_list %}
            {% if row.is_active %}
                <li class="active">  <!-- is_activeの場合にactivteクラスを付与したい-->
            {% else %}
                <li class="">
            {% endif %}
            
            {{ row.value }}
        </li>
        {% endfor %}
    </ul>

上のコードの問題

templateであまりに複雑なコードを書いてしまうのは、ロジックの分離という点で望ましくありません。また上のコードは例えば以下のようなactive以外に共通のクラスがあるケースで冗長なコードが生じます。

    <ul>
        {% for row in data_list %}
            {% if row.is_active %}
                <li class="row active">
            {% else %}
                <li class="row">
            {% endif %}
            
            {{ row.value }}
        </li>
        {% endfor %}
    </ul>

rowが2箇所にあり冗長です。またis_active以外の条件分岐が必要になった場合、あっという間に複雑なtemplateが生まれてしまいます。

これを回避するにはyesnoフィルターが便利です。

yesnoフィルター

yesnoフィルターは以下のように利用します。

{{ value|yesno:'trueの場合の値, falseの場合の値'}}

このコードは以下と同義です。

if value:
    return 'trueの場合の値'
else:
    return 'falseの場合の値'

今回はis_active=Trueのときだけactiveクラスを付与し、そうでない場合はなにもしないので以下のようにカンマの後は何も記載しなければ良いです。(,は必要です)

    <ul>
        {% for row in data_list %}
            <li class="row {{ row.is_active|yesno:'active,'}}">  <!-- is_activeの場合にactivteクラスを付与-->
            {{ row.value }}
        </li>
        {% endfor %}
    </ul>

ifを使った場合と比べるとかなりスッキリしました。この書き方であればis_adminといった属性が増えたとしても以下のようにシンプルなママ書くことができます。

    <ul>
        {% for row in data_list %}
            <li class="
            row 
            {{ row.is_active|yesno:'active,'}} 
            {{ row.is_admin|yesno:'admin,normal'}} 
            ">
            {{ row.value }}
        </li>
        {% endfor %}
    </ul>

template部分はできるだけシンプルに保ちたいので今回のようなケースではyesnoフィルターは便利でいいですね。