[Django]知ってると便利なforで使える小技

Djangoのtemplate内で利用するfor文で知ってるとテンプレートがすっきり書けるかもしれない小技の紹介

まずはシンプルに

"values":["A","B","C"]

{% for value in values %}
  {{ value }}
{% endfor %}

シンプルにリストをforで回しました。
タプルやレンジオブジェクトも同様に回すことが可能です。

辞書のkeyとvalueを同時に

"dic":{"A":"APPLE","B":"BANANA","C":"CHERRY"}

{% for k,v in dic.items %}
  <p>{{ k }}{{ v }}</p>
{% endfor %}

変数を二つ用意してforを回します。

ここまでは普通のPythonとそう変わりはありません。

リストの中身がないときに別の内容を表示

{% for value in values %}
  <p>{{ value }}</p>
{% empty %}
  <p>the list is empty.</p>
{% endfor %}

{% empty %}節を加えることでリストの中身が空だった場合に別の内容を表示できます。
上の場合リストの中身が空であれば<p>the list is empty.</p>が表示されます。

以下のように書いても同じことはできますが、{% empty %}節のほうが見やすいうえに意味も分かりやすいので積極的に上の方法を利用しましょう。

{% if values %}
  {% for value in values %}
    <p>{{ value }}</p>
  {% endfor %}
{% else %}
  <p>the list is empty.</p>
{% endif %}

リストのうちの一部を表示

{% for value in values | slice:"2:5"%}
  <p>{{ value }}</p>

sliceフィルタを用いることで、リストの一部を表示することができます。

上の場合はリストの二番目から四番目までが表示されます。

sliceフィルタの引数を":5"とすれば最初から四番目まで、"2:"とすれば二番目から最後までが表示されます。

ループの最初だけ別の処理を行う

{% for value in values %}
    {% if forloop.first %}
         <p>This is first time. {{ value }}</p>
    {% else %}
         <p>{{ value }}</p>
    {% endif %}
{% endfor %}

forループの中でforloop.firstを用いると一回目のループの時にTrue、それ以外の時にFalseを返します。

if文と組みあわせるとループの最初だけ別の処理をさせることができます。

ループの最後だけ別の処理を行う

{% for value in values %}
    {% if forloop.last %}
         <p>This is last time. {{ value }}</p>
    {% else %}
         <p>{{ value }}</p>
    {% endif %}
{% endfor %}

forloop.lastはループの最後の時にTrue、それ以外の時にFalseを返してくれます。

こちらも、if文と組みあわせるとループの最後だけ別の処理をさせることができます。

forloop.firstforloop.lastは同時に使うこともできます。

ループカウントを取得する

{% for value in values %}
    <p>index:{{ forloop.counter0 }}/
    count:{{ forloop.counter }}/
    value:{{ value }}</p>
{% endfor %}

forloop.counterはループの回数を1からカウントアップします。
一回目のループなら1、二回目のループなら2、N回目ならNになります。

forloop.counter0はループの回数を0からカウントアップします。 一回目のループなら0、二回目のループなら1、N回目ならN-1になります。

ちょうどPythonの組み込み関数のenumerateと同じです。

ループカウントを逆順で取得する

{% for value in values %}
    <p>index:{{ forloop.revcounter0 }}/
    count:{{ forloop.revcounter }}/
    value:{{ value }}</p>
{% endfor %}

forloop.revcounterはループの回数をNからカウントダウンします。
一回目のループならN、二回目のループならN-1、N回目なら1になります。

forloop.revcounter0はループの回数をN-1からカウントダウンします。
一回目のループならN-1、二回目のループならN-2、N回目なら0になります。

それぞれ、forloop.counterforloop.counter0の逆です。

おまけ

勘のいいひとは気づいたかもしれませんが、forloop.counterとif文を組み合わせるとループの特定の回数のところで別の処理をする、といったことが実現できます。

{% for value in values %}
    {%if forloop.counter|divisibleby:"3" %}
      <p>this is a multiple of three. {{ forloop.counter }}</p>
    {% else %}
      <p>{{ value }}</p>
    {% endif %}
{% endfor %}

divisiblebyフィルターと組み合わせれば一定回数ごとに別の処理をするといったこともできます。
上記の例では三回ごとに違う処理をしています。

divisiblebyフィルター 値が引数の値で割り切れる場合に True を返します。

最後に

今回紹介した小技を駆使することでtemplateをすっきり書けるかもしれません。
一度自分の書いたtemplateを見直してみるのもいいかもしれません。

viewとtemplateを分離して書くことができるので、筆者はかなりすっきり書けるようになりました。(viewのほうが)