flask jinja2 全局函数、宏及自定义过滤器

内置全局函数

dict()函数,方便生成字典型变量

{% set user = dict(name='Mike',age=15) %}
<p>{{ user | tojson | safe }}</p>

{# 显示 '{"age": 15, "name": "Mike"}' #}

joiner()函数,神奇的辅助函数。它可以初始化为一个分隔符,然后第一次调用时返回空字符串,以后再调用则返回分隔符。对分隔循环中的内容很有帮助

{% set sep = joiner("|") %}
{% for val in range(5) %}
    {{ sep() }} <span>{{ val }}</span>
{% endfor %}

{# 显示 "0 | 1 | 2 | 3 | 4" #}

cycler()函数,在给定的序列中轮循,列表项

  • 的”class”在”odd”和”even”两个值间轮循

    {% set cycle = cycler('odd', 'even') %}
    <ul>
    {% for num in range(10, 20, 2) %}
        <li class="{{ cycle.next() }}">Number is "{{ num }}",
        next line is "{{ cycle.current }}" line.</li>
    {% endfor %}
    </ul>
    

    “cycler()”函数返回的对象可以做如下操作 * next(),返回当前值,并往下一个值轮循 * reset(),重置为第一个值 * current,当前轮循到的值

    自定义全局函数

    将Flask应用代码中定义的函数,通过”add_template_global”将其传入模板即可

    import re
    def accept_pattern(pattern_str):
        pattern = re.compile(pattern_str, re.S)
        def search(content):
            return pattern.findall(content)
    
        return dict(search=search, current_pattern=pattern_str)
    
    app.add_template_global(accept_pattern, 'accept_pattern')
    

    上例中的accept_pattern函数会先预编译一个正则,然后返回的字典中包含一个查询函数”search”,之后调用”search”函数就可以用编译好的正则来搜索内容了。”app.add_template_global”方法的第一个参数是自定义的全局函数,第二个是全局函数名称。现在,让我们在模板中使用”accept_pattern”全局函数:

    {% with pattern = accept_pattern("<li>(.*?)</li>") %}
      {% set founds = pattern.search("<li>Tom</li><li>Bob</li>") %}
      <ul>
      {% for item in founds %}
        <li>Found: {{ item }}</li>
      {% endfor %}
      </ul>
      <p>Current Pattern: {{ pattern.current_pattern }}</p>
    {% endwith %}
    

    “Tom”和”Bob”被抽取出来了

    Flask同样提供了添加全局函数的装饰器”template_global”,以方便全局函数的添加。我们来用它将取系统当前时间的函数”current_time”定义为全局函数。

    import time
    @app.template_global('end_with')
    def current_time(timeFormat="%b %d, %Y - %H:%M:%S"):
        return time.strftime(timeFormat)
    
    <p>Current Time is: {{ current_time() }}</p>
    <p>Current Day is: {{ current_time("%Y-%m-%d") }}</p>
    

    Flask添加全局函数的方法是封装了对Jinja2环境变量的操作。上述添加”current_time”全局函数的方法,等同于下面的代码。

    app.jinja_env.globals['current_time'] = current_time
    

    宏 (Macro)

    {% macro input(name, type='text', value='') -%}
        <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}">
    {%- endmacro %}
    
    <p>{{ input('username', value='user') }}</p>
    <p>{{ input('password', 'password') }}</p>
    <p>{{ input('submit', 'submit', 'Submit') }}</p>
    

    访问调用者内容

    先来创建个宏”list_users”:

    {% macro list_users(users) -%}
      <table>
        <tr><th>Name</th><th>Action</th></tr>
        {%- for user in users %}
          <tr><td>{{ user.name |e }}</td>{{ caller() }}</tr>
        {%- endfor %}
      </table>
    {%- endmacro %}
    

    调用者的代码:

    {% set users=[{'name':'Tom','gender':'M','age':20},
                  {'name':'John','gender':'M','age':18},
                  {'name':'Mary','gender':'F','age':24}]
    %}
    
    {% call list_users(users) %}
        <td><input name="delete" type="button" value="Delete"></td>
    {% endcall %}
    

    与上例不同,这里我们使用了”{% call %}”语句块来调用宏,语句块中包括了一段生成”Delete”按钮的代码。运行下试试,你会发现每个用户名后面都出现了”Delete”按钮,也就是”{{ caller( ) }}”部分被调用者”{% call %}”语句块内部的内容替代了,个人觉得,主要是有些时候HTML语句太复杂(如上例),不方便写在调用参数上,所以就写在”{% call %}”语句块里了。

    Jinja2的宏不但能访问调用者语句块的内容,还能给调用者传递参数

    首先,我们将表格增加一列性别,并在宏里调用”caller()”方法时,传入一个变量”user.gender”:

    {% macro list_users(users) -%}
      <table>
        <tr><th>Name</th><th>Gender</th><th>Action</th></tr>
        {%- for user in users %}
          <tr><td>{{ user.name |e }}</td>{{ caller(user.gender) }}</tr>
        {%- endfor %}
      </table>
    {%- endmacro %}
    

    修改下调用者语句块:

    {% call(gender) list_users(users) %}
        <td>
        {% if gender == 'M' %}
        <img src="{{ url_for('static', filename='img/male.png') }}" width="20px">
        {% else %}
        <img src="{{ url_for('static', filename='img/female.png') }}" width="20px">
        {% endif %}
        </td>
        <td><input name="delete" type="button" value="Delete"></td>
    {% endcall %}
    

    我们在使用”{% call %}”语句时,将其改为了”{% call(gender) … %}”,这个括号中的”gender”就是用来接受宏里传来的”user.gender”变量。因此我们就可以在”{% call %}”语句中使用这个”gender”变量来判断用户性别。这样宏就成功地向调用者传递了参数。

    宏的内部变量

    宏的内部可以使用”caller( )”方法获取调用者的内容。此外宏还提供了两个内部变量:

    varargs

    这是一个列表。如果调用宏时传入的参数多于宏声明时的参数,多出来的没指定参数名的参数就会保存在这个列表中。

    kwargs

    这是一个字典。如果调用宏时传入的参数多于宏声明时的参数,多出来的指定了参数名的参数就会保存在这个字典中。

    {% macro input(name, type='text', value='') -%}
        <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}">
        <br /> {{ varargs }}
        <br /> {{ kwargs }}
    {%- endmacro %}
    <p>{{ input('submit', 'submit', 'Submit', 'more arg1', 'more arg2', ext='more arg3') }}</p>
    

    可以看到,varargs变量存了参数列表”[‘more arg1’, ‘more arg2’]”,而kwargs字典存了参数”{‘ext’:’more arg3′}”。

    当”include”的模板文件不存在时,程序会抛出异常。你可以加上”ignore missing”关键字,这样如果模板不存在,就会忽略这段”{% include %}”语句。

    {% include 'footer.html' ignore missing %}
    
       {% include ['footer.html','bottom.html','end.html'] ignore missing %}
    

    上例中,程序会按顺序寻找模板文件,第一个被找到的模板即被加载,而其后的模板都会被忽略。如果都没找到,那整个语句都会被忽略。