web-dev-qa-db-ja.com

Jinja変数のスコープは、内部ブロックを超えて拡張できますか?

次のJinjaテンプレートがあります。

{% set mybool = False %}
{% for thing in things %}
    <div class='indent1'>
        <ul>
            {% if current_user %}
              {% if current_user.username == thing['created_by']['username'] %}
                {% set mybool = True %}
                <li>mybool: {{ mybool }}</li> <!-- prints True -->
                <li><a href='#'>Edit</a></li>
              {% endif %}
            {% endif %}
            <li>Flag</li>
        </ul>
    </div>
    <hr />
{% endfor %}

{% if not mybool %}
    <!-- always prints this -->
    <p>mybool is false!</p>
{% else %}
  <p>mybool is true!</p>
{% endif %}

forループで条件が満たされた場合、myboolをtrueに変更して、mybool is true!以下。ただし、内部myboolのスコープはifステートメントに制限されているように見えるため、desiredmyboolは設定しないでください。

最後のmyboolステートメントで使用できるように「グローバル」ifを設定するにはどうすればよいですか?

[〜#〜] edit [〜#〜]

一部の提案 (キャッシュされたページビューのみが正しく表示される)が見つかりましたが、機能していないようです。おそらく、Jinja2で非推奨になりました...

[〜#〜] edit [〜#〜]

以下にソリューションを提供します。上記の提案がなぜうまくいかないのか、私はまだ興味があります。誰もが彼らが廃止されたことを確かに知っていますか?

54
Matt Norris

この制限を回避する1つの方法は、 "do" expression-statement extension を有効にし、ブール値の代わりに配列を使用することです:

_{% set exists = [] %}
{% for i in range(5) %}
      {% if True %}
          {% do exists.append(1) %}
      {% endif %}
{% endfor %}
{% if exists %}
    <!-- exists is true -->
{% endif %}
_

Jinjaの「do」式ステートメント拡張機能を有効にするには:e = jinja2.Environment(extensions=["jinja2.ext.do",])

47
Garrett

関連する質問への回答:テンプレートに特定のifブロックを入力した回数のグローバルカウンターが必要でしたが、最終的には次のようになりました。

テンプレートの上部:

{% set counter = ['1'] %}

Ifブロックでカウントしたい:

{% if counter.append('1') %}{% endif %}

カウントを表示する場合:

{{ counter|length }}

文字列'1'は任意の文字列または数字に置き換えることができると思います。それはまだハックですが、それほど大きなものではありません。

14
Godsmith

このハック(拡張機能なし)を使用して問題を解決できます。

import jinja2

env = jinja2.Environment()
print env.from_string("""
{% set mybool = [False] %}
{% for thing in things %}
    <div class='indent1'>
        <ul>
            {% if current_user %}
              {% if current_user.username == thing['created_by']['username'] %}
                {% set _ = mybool.append(not mybool.pop()) %}
                <li>mybool: {{ mybool[0] }}</li> <!-- prints True -->
                <li><a href='#'>Edit</a></li>
              {% endif %}
            {% endif %}
            <li>Flag</li>
        </ul>
    </div>
    <hr />
{% endfor %}

{% if not mybool[0] %}
    <!-- always prints this -->
    <p>mybool is false!</p>
{% else %}
  <p>mybool is true!</p>
{% endif %}
""").render(current_user={'username':'me'},things=[{'created_by':{'username':'me'}},{'created_by':{'username':'you'}}])
8
Alvaro Fuentes

2018年に更新

Jinja 2.1 (2017年11月8日)現在、この特定の問題に対処するnamespace()オブジェクトがあります。詳細と例については、公式の Assignmentsdocumentation を参照してください。 class documentation は、いくつかの値を名前空間に割り当てる方法を示しています。

7
Jens

contextfunction()または類似の何かを書くとき、あなたはコンテキストがあなたがそれを修正するのを止めようとすることに気づくかもしれません。

内部コンテキストAPIを使用してコンテキストを変更できた場合、コンテキストの変更がテンプレートに表示されないように思われるかもしれません。これは、Jinjaがパフォーマンス上の理由からテンプレート変数のプライマリデータソースとしてのみコンテキストを使用するためです。

コンテキストを変更する場合は、setを使用して変数に割り当てる代わりに変数を返す関数を作成します。

{% set comments = get_latest_comments() %}

ソース

4
Shankar Cabus

リスト(objects_from_db)からオブジェクト(オブジェクト)のエントリの最大数を見つける必要がありました。

これは、jinja2および変数スコープで知られている理由で機能しませんでした。

 {% set maxlength = 0 %}
 {% for object in objects_from_db %}
     {% set ilen = object.entries | length %}
     {% if maxlength < ilen %}
         {% set maxlength = ilen %}
     {% endif %}
 {% endfor %}

動作するものは次のとおりです。

 {% set mlength = [0]%}
 {% for object in objects_from_db %}
     {% set ilen = object.entries | length %}
     {% if mlength[0] < ilen %}
         {% set _ = mlength.pop() %}
         {% set _ = mlength.append(ilen)%}
     {% endif %}
 {% endfor %}
 {% set maxlength = mlength[0] %}

これが他の誰かが同じことを理解しようとしているのを助けることを願っています。

3
Paddy V

このすばらしい 記事 が見つかりました。別のスコープでjinja変数の値を変更することはできませんが、グローバル辞書の値を変更することはできます。

# works because dictionary pointer cannot change, but entries can 

{% set users = ['alice','bob','eve'] %} 
{% set foundUser = { 'flag': False } %} 

initial-check-on-global-foundUser: 
  cmd.run: 
    name: echo initial foundUser = {{foundUser.flag}} 

{% for user in users %} 
{%- if user == "bob" %} 
{%-   if foundUser.update({'flag':True}) %}{%- endif %} 
{%- endif %} 
echo-for-{{user}}: 
  cmd.run: 
    name: echo my name is {{user}}, has bob been found? {{foundUser.flag}} 
{% endfor %} 

final-check-on-global-foundUser: 
  cmd.run: 
    name: echo final foundUser = {{foundUser.flag}}

また、実際にsetを使用せずに値を設定するために、この構文が非常に役立つこともわかりました。

{%-   if foundUser.update({'flag':True}) %}{%- endif %} 

実際には、辞書に対するupdate操作の結果をチェックします(自己注意)。

0
Maciejg