0
0

Jinjaのselectattrが返すのはジェネレータであることにハマった

Last updated at Posted at 2024-01-19

あらまし

Jinjaselectattrフィルターにハマったのでメモ。

困ったこと

import jinja2
params = [
    {
        'class': 'A',
        'name': '太郎',
    },
    {
        'class': 'B',
        'name': '花子',
    },
]
env = jinja2.Environment()
tmpl = env.from_string('''
    {%- if params | selectattr('class','==','C') %}
        Cクラスの人はいますね
    {%- endif %}
''')
print(tmpl.render(params))

とすると、Cクラスの人はいますね と表示されてしまう。

なぜこうなるのか

selectattr が返すのはジェネレータであるため。

tmpl = env.from_string('''
    {{ params | selectattr('class','==','C') }}
''')
print(tmpl.render(params))

<generator object select_or_reject at 0x000001DBFB6A3140>

if は空リストであれば偽と判定するが、
今回の場合はジェネレータオブジェクトを渡しており、これ自体は偽と判定されない。
(ジェネレータであることと、ジェネレータが何も要素を返さないのは別の話)

対策

list フィルターに通すのがいいかと思う。
これでselectattrの結果要素が見つからなかった場合は空リストになり、
ifで偽と判定できる。

tmpl = env.from_string('''
    {%- if params | selectattr('class','==','C') | list %}
        Cクラスの人はいますね
    {%- endif %}
''')
print(tmpl.render(params))

⇒何も表示されない。

別解

独自の test を実装してもいいかも。

def existing(itr):
   try:
      next(iter(itr))
   except StopIteration:
      return False
   else:
      return True
env.tests['existing'] = existing
tmpl = env.from_string('''
    {%- if params | selectattr('class','==','C') is existing %}
        Cクラスの人はいますね
    {%- endif %}
''')
print(tmpl.render(params))

今見てみたら

ドキュメントに「Similar to a generator comprehension such as:」とか書いてあった・・・
でもこれでselectattrが返すのはジェネレータだと気付くのは無理じゃない・・・?

今後のために

普段のPythonコーディングで気にしていることだが、
Jinjaにおいても型を意識することは重要である。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0