アーカイブページの構成を変更しました

Posted on 2020-03-18 in zakki

アーカイブページの構成を変更しました。ただ、構成を変更したとは言いつつそこまで大したことはしておらず、

  • 作成日付と記事のタイトルの表示位置を少し見直した
  • アーカイブページ内の西暦を動的に表示させるようにした

の 2 点を修正しています。

動的=記事が作成された西暦に応じて表示させる、というものなのですが今回こちらを実装するにあたり Jinja をこねくり回しました。こねくり回す中で使用した Jinja の namespace についてメモしておきます。

変数のスコープ

実装するにあたり最初はこのような構成のコードを書いたのですが、想定とは異なる動きとなりました。

{% set word = "hoge" %}
{% set numbers = [1, 2, 3] %}
{% for n in numbers %}
  {% if n == 1 %}
    {% set word = "fuga" %}
    {{ word }}
  {% endif %}
{% endfor %}
{{ word }}

# > fuga fuga と表示されることを期待していたのですが …
# > fuga hoge となってしまう

色々と確認していたところ、Jinja において for ループの内側の変数のスコープはループ内に留まるようです。

namespace の使用

この問題を解決するために Jinja2 2.10 から導入されている namespace というクラスを用いました。(参考)

The main purpose of this is to allow carrying a value from within a loop body to an outer scope.

と参考リンクの公式文中にもある通り {% set %} を用いて変数の上書きを許容させるようにする方法のようです。

{% set ns = namespace(word = "hoge") %}
{% set numbers = [1, 2, 3] %}
{% for n in numbers %}
  {% if n == 1 %}
    {% set ns.word = "fuga" %}
    {{ ns.word }}
  {% endif %}
{% endfor %}
{{ ns.word }}

# > fuga fuga

と for ループの外側においても set で定義した変数が参照できるようになりました。

実際のコード

このクラスを利用して、現在のアーカイブページを構成する Jinja テンプレートのコードは以下のようにしています。 article.datedatetime オブジェクトです。さらに strftime('%Y') で年数のみを表示させています。(参考)

{% set ns = namespace(year=False) %}
{% for article in dates %}
  {% if article.date|strftime('%Y') != ns.year %}
    {% set ns.year = article.date|strftime('%Y') %}
    <h4>{{ ns.year }}年 </h4>
  {% endif %}
    <dd>{{ article.locale_date }}: 
      <a href=" {{ SITEURL }}/{{ article.url }}">{{ article.title }}</a></dd> 
{% endfor %}

サイト Pelican Jinja