Nice programing

Jinja 변수의 범위가 내부 블록을 넘어 확장 될 수 있습니까?

nicepro 2020. 12. 30. 20:22
반응형

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있으므로 원하는 mybool 것이 설정되지 않은 것 같습니다.

mybool마지막 if에서 사용할 수 있도록 "전역"을 어떻게 설정할 있습니까?

편집하다

몇 가지 제안을 찾았 지만 (캐시 된 페이지보기 만 올바르게) 작동하지 않는 것 같습니다. 아마도 그들은 Jinja2에서 더 이상 사용되지 않을 것입니다 ...

편집하다

아래에 제공된 솔루션입니다. 위의 제안이 작동하지 않는 이유가 여전히 궁금합니다. 누구든지 그들이 더 이상 사용되지 않는다는 것을 알고 있습니까?


이 제한을 우회하는 한 가지 방법은 "do"표현식 문 확장 을 활성화하고 부울 대신 배열을 사용하는 것입니다.

{% 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",])


관련 질문에 대한 답변 : 템플릿에 특정 if 블록을 입력 한 횟수에 대한 글로벌 카운터를 갖고 싶었고 결국 아래와 같이되었습니다.

템플릿 상단 :

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

if 블록에서 계산하고 싶습니다.

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

카운트를 표시 할 때 :

{{ counter|length }}

문자열 '1'은 어떤 문자열이나 숫자로도 바꿀 수 있습니다. 여전히 해킹이지만 그다지 크지는 않습니다.


이 해킹 (확장 프로그램없이)을 사용하여 문제를 해결할 수 있습니다.

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'}}])

2018 업데이트

As of Jinja 2.10 (8th Nov 2017) there is a namespace() object to address this particular problem. See the official Assignments documentation for more details and an example; the class documentation then illustrates how to assign several values to a namespace.


When writing a contextfunction() or something similar you may have noticed that the context tries to stop you from modifying it.

If you have managed to modify the context by using an internal context API you may have noticed that changes in the context don’t seem to be visible in the template. The reason for this is that Jinja uses the context only as primary data source for template variables for performance reasons.

If you want to modify the context write a function that returns a variable instead that one can assign to a variable by using set:

{% set comments = get_latest_comments() %}

Source


Had a need to find the max num of entries in an object (object) from a list (objects_from_db),

This did not work for reasons known in jinja2 and variable scope.

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

Here's what works:

 {% 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] %}

Hope this helps someone else trying to figure out the same.


Found this great article that describes a little hack. It's not possible to change value of a jinja variable in a different scope, but it's possible to modify a global dictionary values:

# 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}}

I've also found very helpful this syntax to set the value without actually using set:

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

It actually checks the result of an update operation on a dictionary (note to self).


Here's the general case for anyone wanting to use the namespace() object to have a variable persist outside of a for loop.

{% set accumulator = namespace(total=0) %}
{% for i in range(0,3) %}
    {% set accumulator.total = i + accumulator.total %}
    {{accumulator.total}}
 {% endfor %}`          {# 0 1 3 #}
 {{accumulator.total}}  {# 3 (accumulator.total persisted past the end of the loop) #}

ReferenceURL : https://stackoverflow.com/questions/4870346/can-a-jinja-variables-scope-extend-beyond-in-an-inner-block

반응형