Jinja2,由Flask框架的创作者开发,是一款功能丰富的模板引擎,以其完整的Unicode支持、灵活性、高效性和安全性而备受推崇。最初受Django模板引擎启发,Jinja2为Flask提供了强大的模板支持,后来也成为其他项目的首选。在本文中,我们将深入探讨Jinja2的特性、语法以及如何在Flask应用中使用它来构建动态而又美观的Web页面。

IF模板

IF语句用于在模板中执行条件判断,根据不同的条件呈现不同的内容。在模板中,通过{% if condition %} ... {% endif %}的语法结构来定义条件块。其中,condition是一个表达式或变量,如果该条件为真,模板引擎将执行if块内的内容,否则将跳过。

IF模板语句支持多种条件判断,包括比较运算、逻辑运算等。通过合理运用IF语句,可以根据动态数据或用户输入,在页面上展示不同的信息或呈现不同的页面结构,从而实现更加灵活和个性化的页面设计。

// ----------------------------------------------
// 前端部分
// ----------------------------------------------
{% if username and username == "admin" %}
    <h1>hello {{ username }} welcome</h1>
{% elif username and username == "lyshark" %}
    <h1>hello {{ username }}</h1>
{% else %}
    <h1>hello lyshark</h1>
{% endif %}

// ----------------------------------------------
// 后端部分
// ----------------------------------------------
@app.route("/", methods=["GET", "POST"])
def index():
    return render_template("index.html",username = "admin")

FOR模板

FOR循环模板语句允许在模板中对数据进行迭代操作,便于遍历集合、列表或字典等数据结构,并在模板中对每个元素执行相同的操作。通过{% for item in iterable %} ... {% endfor %}的语法结构,可以定义一个FOR循环块。

在FOR循环中,item表示每次迭代中当前的元素,而iterable则是要遍历的数据集合。循环块内的代码将在每次迭代时执行,允许动态生成页面内容。此外,Jinja2的FOR循环还支持循环索引、循环计数等功能,提供了灵活的迭代控制机制。

FOR模板语句在Web开发中经常用于动态生成页面元素,特别是在展示多条数据、列表或表格内容时非常实用。通过FOR循环,开发者可以更方便地处理和展示动态数据,提高页面的灵活性和交互性。

// ----------------------------------------------
// 前端部分
// ----------------------------------------------
<!--输出列表-->
{% for x in digits %}
    <p>输出列表: {{ x }}</p>
{% endfor %}

<!--输出字典-->
{% for item in dicts %}
    <!--第一个和最后一个元素-->
    {% if loop.first %}
        <b>第一个元素是: {{ item.name }}</b>
    {% elif loop.last %}
        <b>最后一个元素是: {{ item.name }}</b>
    {% endif %}

    <!--下标输出-->
    <b>当前下标(从1开始): {{ loop.index }}</b>
    <b>当前下标(从0开始): {{ loop.index0 }}</b>

    <!--字典长度-->
    <b>数组长度: {{ loop.length }}</b>
    <b>迭代计数(从1开始): {{ loop.revindex }}</b>
    <b>迭代计数(从0开始): {{ loop.revindex0 }}</b>

    <p> 输出名字: {{ item.name }} 输出密码: {{ item.password }}</p>
{% endfor %}

<!--输出元组-->
{% for href,caption in tuples %}
    <p>{{ href }} = {{ caption }}</p>
{% endfor %}

// ----------------------------------------------
// 后端部分
// ----------------------------------------------
@app.route("/", methods=["GET", "POST"])
def index():
    return render_template("index.html",
                           digits=[1,2,3,4,5],
                           dicts=[
                                {'name':'John','password':'123123'},
                                {'name':'Tom', 'password':'123456'},
                                {'name':'Lisa', 'password':'123123'},
                                {'name':'Bob', 'password':'123456'}
                            ],
                           tuples=[
                                ('index.html', 'Index'),
                                ('about.html', 'About'),
                                ('downloads.html', 'Downloads')]
                           )

FOR模板排序允许在模板中对循环输出的元素进行排序或分组操作,通过内置的过滤器实现。这提供了更灵活的控制机制,使得模板能够按照特定的顺序展示数据,或者将数据按照某个条件分组呈现。

通过结合FOR循环和排序过滤器,模板可以根据开发者的需求对数据进行动态排列。在模板中,使用类似于{% for item in iterable | sort %} ... {% endfor %}的语法,可以对iterable中的元素进行排序。除了简单的字母和数字排序外,Jinja2还支持通过自定义函数进行排序,提供了更高度定制的排序功能。

// ----------------------------------------------
// 前端部分
// ----------------------------------------------
<!-- 按指定字段排序,这里设reverse为true使其按降序排 -->
<ul>
    {% for item in dicts | sort(attribute='age', reverse=true) %}
         <li> 名字: {{ item.name }} 年龄: {{ item.age }}</li>
    {% endfor %}
</ul>

<!-- 列表分组,每组是一个子列表,组名就是分组项的值 -->
<ul>
    {% for group in dicts|groupby('gender') %}
        <li>组名: {{ group.grouper }}
            <ul>
                {% for user in group.list %}
                    <li>用户: {{ user.name }}</li>
                {% endfor %}
            </ul>
        </li>
    {% endfor %}
</ul>

<!-- 取字典中的某一项组成列表,再将其连接起来 -->
<p>连接后: {{ dicts | map(attribute='name') | join(', ') }}</p>
<p>连接后: {{ dicts | map(attribute='age') | join(', ') }}</p>

// ----------------------------------------------
// 后端部分
// ----------------------------------------------
@app.route("/", methods=["GET", "POST"])
def index():
    return render_template("index.html",
                           dicts = [
                                {'name':'Tom','gender':'M','age':20},
                                {'name':'John','gender':'M','age':18},
                                {'name':'Mary','gender':'F','age':24},
                                {'name':'Bob','gender':'M','age':31},
                                {'name':'Lisa','gender':'F','age':19}]
                           )

if __name__ == '__main__':
    app.run(debug=True)

模板闪现机制

闪现机制是一种在请求之间传递信息的有效方式。它允许在一个请求中设置数据,然后在下一个请求中访问这些数据。主要通过flash()函数实现,使开发者能够方便地在请求之间传递和呈现信息,增强了用户体验。

闪现机制通过flash()函数实现,主要分为三种:

  1. 基本闪现机制: 使用flash(message, category='message')函数将消息闪现到下一个请求。这个消息可以是字符串,也可以是其他数据类型,而category参数用于指定消息的类别,通常默认为'message'。
  2. 消息分类: 闪现消息可以根据不同的类别进行分类,以便在前端页面中有更好的呈现形式。通过设置category参数,可以将消息划分为不同的类别,例如'error'、'success'等,以便在模板中有条件地处理这些消息。
  3. 模板中的处理: 在模板中,可以使用get_flashed_messages(with_categories=False, category_filter=['error', 'warning'])函数获取所有闪现的消息。通过with_categories参数可以选择获取消息时是否携带类别信息,而category_filter参数则可以指定只获取特定类别的消息。

这三种机制共同构成了Flask框架中灵活且强大的模板闪现系统,使得在Web应用中更便捷地实现消息传递和呈现。

1.模板中获取闪现信息,实现在下次请求时返回内容到前台。

<!--闪现消息,返回一个列表-->
{% with messages = get_flashed_messages() %}
  {% if messages %}
    <ul>
        {% for message in messages %}
          <li>{{ message }}</li>
        {% endfor %}
    </ul>
  {% endif %}
{% endwith %}


{% if error %}
    <p><strong>失败消息:</strong> {{ error }}</p>
{% endif %}

<form action="" method=post>
    用户名: <input type=text name=username>
    密码: <input type=password name=password>
    <input type=submit value="用户登录">
</form>

后端只需要在验证通过的情况下,直接调用flash()函数实现消息的前台传递。

from flask import Flask, flash, redirect, render_template, request, url_for

app = Flask(__name__, template_folder="./tempate",static_folder="./tempate")
app.secret_key = 'some_secret'

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == "POST":
        if request.form['username'] != "lyshark" or request.form['password'] != "1233":
            flash("登录失败了")
            return render_template('index.html',error = "失败")
        else:
            flash("恭喜您登录成功")

    return render_template('index.html')

if __name__ == "__main__":
    app.run()

2.模板中的分类闪现,在闪现消息是指定一个消息分了i,如果不指定则默认分类为Message消息。

要使用自定义的分类,只要使用flash()函数传入第二个参数即可。

{% with messages = get_flashed_messages(with_categories=true) %}
  {% if messages %}
    <ul>
        {% for category, message in messages %}
          <li class="{{ category }}">{{ category }}:{{ message }}</li>
        {% endfor %}
    </ul>
  {% endif %}
{% endwith %}

后端只需要增加第二个参数来指定需要显现的分组即可。

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == "POST":
        if request.form['username'] != "lyshark" or request.form['password'] != "1233":
            flash("登录失败了")
            return render_template('index.html',error = "失败")
        else:
            flash("恭喜您登录成功","status")
            flash("测试账户","username")

    return render_template('index.html')

if __name__ == "__main__":
    app.run()

3.模板中过滤闪现消息,过滤闪现即指在前台通过category_filter增加过滤条件,来实现对特殊消息的过滤输出。

<!--闪现消息,增加过滤器-->
{% with messages = get_flashed_messages(category_filter=["username","status"]) %}
  {% if messages %}
    <ul>
        {% for message in messages %}
          <li>{{ message }}</li>
        {% endfor %}
    </ul>
  {% endif %}
{% endwith %}

{% if error %}
    <p><strong>失败消息:</strong> {{ error }}</p>
{% endif %}

<form action="" method=post>
    用户名: <input type=text name=username>
    密码: <input type=password name=password>
    <input type=submit value="用户登录">
</form>

后端也可在闪现消息时指定一个category属性实现过滤机制。

from flask import Flask, flash, redirect, render_template, request, get_flashed_messages

app = Flask(__name__, template_folder="./tempate",static_folder="./tempate")
app.secret_key = 'some_secret'

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == "POST":
        if request.form['username'] != "lyshark" or request.form['password'] != "1233":
            flash("登录失败了")
            return render_template('index.html',error = "失败")
        else:
            flash("恭喜您登录成功","status")
            flash("测试新的闪现",category="username")       # 输出到username
            flash("测试账户","username")                    # 闪现到username

    print('闪现的信息是{}'.format(get_flashed_messages()))
    return render_template('index.html')

if __name__ == "__main__":
    app.run()

自定义上下文

上下文是一个在请求处理过程中可以访问的全局对象集合。除了Flask本身提供的默认上下文之外,开发者还可以通过自定义上下文函数来添加额外的全局变量或函数,以便在视图函数和模板中使用。这就是自定义上下文函数的作用。

关键点和优势:

  1. 上下文函数的定义: 自定义上下文函数是通过app.context_processor装饰器定义的。这个函数会在每次请求处理前被调用,返回的字典中的键值对将成为全局变量。
  2. 全局变量的添加: 开发者可以在自定义上下文函数中添加一些全局变量,这些变量可以在所有视图函数和模板中直接访问,无需在每个视图函数中都进行传递。
  3. 公共函数的注入: 除了变量,还可以在自定义上下文函数中注入一些公共函数。这些函数可以用于处理数据、生成通用的HTML片段等。
  4. 模板中的使用: 自定义上下文函数中添加的变量可以直接在模板中使用,而无需在每个视图函数中都传递一遍。这简化了代码,提高了开发效率。
  5. 适用于多个视图: 自定义上下文函数中添加的内容对整个应用程序的多个视图都是可用的,因此非常适合用于那些需要在整个应用范围内共享的信息。

通过合理使用自定义上下文函数,可以使Flask应用更加灵活、可维护,并提供一致的全局信息和功能。这种机制有助于将一些通用的操作和数据注入到应用中,提高了代码的可读性和可重用性。

下面我们就来先定义一个上下文变量以及上下文函数,将返回字典的属性指向一个函数即可。

<p>当前APP名字: {{ app_name }}</p>
<p>当前时间戳: {{ local_time }}</p>
<p>当前名字: {{ local_user }}</p>

<p>当前时间: {{ current_time() }}</p>
<p>格式化输出: {{ current_time("%Y-%m-%d") }}</p>

<p> 传递整数调用函数: {{ add_function(34,12) }}</p>
<p> 传递数组调用函数: {{ list_function([1,2,3,4,5]) }}</p>

后端代码编写部分,需要引入current_app然后在需要定义的函数上增加@app.context_processor装饰器,返回参数通过字典调用dict(list_function = get)得到结果。

from flask import Flask, render_template
from flask import current_app
import time

app = Flask(__name__)

# 自定义变量
@app.context_processor
def appinfo():
    return dict(app_name = current_app.name,
                local_time = time.time(),
                local_user = "lyshark"
                )

# 自定义函数
@app.context_processor
def app_function_gettime():
    def get_time(timeFormat="%b %d, %Y - %H:%M:%S"):
        return time.strftime(timeFormat)
    return dict(current_time = get_time)

# 自定义函数(传递整数)
@app.context_processor
def app_function_add():
    def get(x=0,y=0):
        z = x + y
        return z
    return dict(add_function = get)

# 自定义函数(传递数组)
@app.context_processor
def app_function_list():
    def get(x=[]):
        sum = 0
        for item in x:
            sum = sum + item
        return sum
    return dict(list_function = get)

@app.route("/", methods=["GET", "POST"])
def index():
    return render_template("index.html")

if __name__ == '__main__':
    app.run(debug=True)

自定义过滤器

自定义过滤器是一种强大的工具,允许开发者在模板中对数据进行各种处理和格式化操作。过滤器其实就是函数,通过使用add_template_filter方法将自定义函数注册为模板过滤器,从而在模板中调用。

关键点和优势:

  1. 过滤器的定义: 开发者可以通过定义一个函数,并使用add_template_filter方法将这个函数注册为模板过滤器。这个函数将用于对模板中的数据进行处理。
  2. 数据处理和格式化: 自定义过滤器可以执行各种数据处理和格式化操作,如日期格式化、字符串截断、数据转换等。这有助于在模板中减少逻辑处理,保持模板的简洁性。
  3. 可重用性: 通过自定义过滤器,开发者可以将常用的数据处理逻辑抽象成函数,提高代码的可重用性。这些过滤器可以在多个模板和视图中共享使用。
  4. 模板中的使用: 一旦注册了自定义过滤器,就可以在模板中使用它。通过在模板中调用过滤器函数,并传递相应的参数,可以对模板中的数据进行实时处理。
  5. 框架集成: Flask提供了简单而强大的方式来集成自定义过滤器,使得开发者可以轻松地扩展模板引擎的功能,满足不同场景下的需求。

通过灵活使用自定义过滤器,可以使模板引擎更加强大,满足更复杂的展示需求。这种机制有助于降低模板中的代码复杂度,提高开发效率,同时保持模板的可读性。

过滤器其实是一个函数,函数支持自定义功能,通过flask的add_template_filter将我们的函数加入到过滤器表单中。

<p>输出双数: {{ [1,2,3,4,5,6,7,8,9,10] | double_step }}</p>
<p>输出子列表: {{ [1,2,3,4,5,6,7,8,9,10] | sub_step(1,5) }}</p>
<p>全局函数调用: {{ global_add(10,20,30) }} </p>

后台增加过滤器有两种方式,直接在函数上增加@app.template_filter('sub_step')装饰器实现,或通过add_template_filter将某个函数直接定义为过滤器使用。

from flask import Flask, render_template
from flask import current_app

app = Flask(__name__)

@app.route("/", methods=["GET", "POST"])
def index():
    return render_template("index.html")

# 增加过滤器(方式1),输出列表中的双数
def double_step_filter(x):
    return x[::2]
app.add_template_filter(double_step_filter,"double_step")

# 增加过滤器(方式2)
@app.template_filter('sub_step')
def sub(x, start, end):
    return x[start:end]

# 增加全局函数
@app.template_global('global_add')
def function(x,y,z):
    return x+y+z

if __name__ == '__main__':
    app.run(debug=True)

自定义测试器

自定义测试器是一种用于在模板中进行条件判断的工具。类似于过滤器,自定义测试器也是通过注册函数的方式来实现的,但是它们主要用于在模板中执行布尔条件测试。

关键点和优势:

  1. 测试器的定义: 开发者可以定义一个函数,并使用@app.template_test()装饰器将其注册为模板测试器。这个函数将包含一些布尔条件判断逻辑。
  2. 条件判断: 自定义测试器主要用于在模板中进行条件判断。通过在模板中调用测试器函数,并传递相应的参数,可以获取布尔值,用于控制模板中的条件分支。
  3. 可读性和模块化: 将常用的布尔条件判断逻辑抽象成测试器函数,有助于提高模板中的可读性和模块化程度。这使得在模板中的条件判断部分更为清晰和易于维护。
  4. 模板中的使用: 一旦注册了自定义测试器,就可以在模板中使用它。通过在模板中调用测试器函数,并传递相应的参数,可以获得布尔值,从而决定模板中的条件分支。
  5. 框架集成: Flask提供了简单而强大的方式来集成自定义测试器,使得开发者可以轻松地扩展模板引擎的功能,实现更灵活的条件判断。

通过合理使用自定义测试器,可以使得模板中的条件判断更为清晰和灵活。这种机制有助于降低模板中的代码复杂度,提高开发效率,同时使得模板的逻辑更易于理解和维护。

自定义测试器与过滤器基本一致,区别在于测试器使用@app.template_test()装饰函数,其他方法与过滤器保持一致。

<!--验证字符串结尾是否是指定字符-->
{% if name is end_with "me" %}
  <h2>"字符串 {{ name }}" 结尾是 "me"</h2>
{% else %}
    <h2>"字符串 {{ name }}" 结尾不是 "me"</h2>
{% endif %}

<!--验证数组中是否有大于10的元素-->
{% if lists is array_of "10" %}
  <h2>列表中存在,大于10的数据</h2>
{% endif %}

测试器后端分别定义两个函数,一个用于判断字符串结尾是否存在某个字符,另一个则是验证数组内是否存在大于元素。

from flask import Flask, render_template

app = Flask(__name__)

# 自定义测试器
@app.template_test('end_with')
def end_with(str,suffix):
    return str.lower().endswith(suffix.lower())

# 自定义测试器(测试数组内是否有大于某值的)
@app.template_test('array_of')
def array_of(str,suffix):
    for item in str:
        if item >= int(suffix):
            return True

@app.route("/", methods=["GET", "POST"])
def index():
    return render_template("index.html",name = "lyshark me", lists = [1,2,3,4,5,6,7,8,56,33])

if __name__ == '__main__':
    app.run(debug=True)