控制语句

在模板中,也存在 if 判断和 for 循环等控制语句。所有的控制语句都是放在 {% %} 中间的,并且在控制语句结束后,要加入相应的结束语句。下面对 if 判断语句和 for 循环语句分别进行讲解。

if判断语句

Jinja2 中的 if 判断语句和 Python 中的 if 判断语句非常类似,可以使用关系运算符 >、<、>=、<=、==、!= 来进行判断,也可以通过 and、or、not 来进行逻辑操作。我们首先创建一个视图函数 if_statement,代码如下。

@app.route("/if")
def if_statement():
    age = 18
    return render_template("if.html", age=age)

以上代码定义了一个 age 变量,并且把这个 age 传给了 if.html 模板。在 if.html 模板中,可以根据 age 的大小判断是否成年。if.html 模板的代码如下。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>if语句</title>
  </head>
  <body>
    {% if age > 18 %}
      <div>您已成年!</div>
    {% elif age < 18 %}
      <div>您未成年!</div>
    {% else %}
      <div>您刚成年!</div>
    {% endif %}
  </body>
</html>

因为在视图函数中给 age 赋值为 18,所以在模板中会匹配到以下代码。

<div>您刚成年!</div>

在浏览器中访问 http://127.0.0.1:5000/if ,也可以看到显示的是 “您刚成年!”,如图 4-5 所示。

image 2025 01 21 12 26 56 761
Figure 1. 图4-5 模板中使用if判断语句

仔细阅读 if.html 模板代码可以发现,在 if 语句结束后,需要添加 endif 关闭 if 代码块,这跟 Python 中的用法是有点不同。

Jinja2 中的代码缩进只是为了更加方便阅读,任何缩进都不是必需的。

for循环语句

Jinja2 中的 for 循环与 Python 中的 for 循环也非常类似,Jinja2 中的 for 循环只是比 Python 中的 for 循环多了一个 endfor 代码块。我们先来实现一下视图函数 for_statement,代码如下。

@app.route("/for")
def for_statement():
    books = [
        {
            "name": "三国演义",
            "author": "罗贯中",
            "price": 100
        },
        {
            "name": "水浒传",
            "author": "施耐庵",
            "price": 99
        },
        {
            "name": "红楼梦",
            "author": "曹雪芹",
            "price": 101
        },
        {
            "name": "西游记",
            "author": "吴承恩",
            "price": 102
        }
    ]
    return render_template("for.html", books=books)

在 for_statement 视图函数中,首先创建了一个 books 变量,books 是一个列表,里面存放的是图书信息的字典,然后渲染给 for.html 模板。接下来在模板文件中循环这个列表,代码如下。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>for 循环</title>
</head>
<body>
  <table>
    <thead>
      <tr>
        <th>书名</th>
        <th>作者</th>
        <th>价格</th>
      </tr>
    </thead>
    <tbody>
      {% for book in books %}
        <tr>
          <td>{{ book.name }}</td>
          <td>{{ book.author }}</td>
          <td>{{ book.price }}</td>
        </tr>
      {% endfor %}
    </tbody>
  </table>
</body>
</html>

因为在 table 表格标签中,一个 tr 标签代表表格中的一行,所以在 tr 外面加了一个 for 循环,去循环这个 books 列表。在 tr 下面,一个 td 代表一列,每列从 book 中获取对应的数据,分别是书名、作者、价格。在浏览器中访问 http://127.0.0.1:5000/for ,显示结果如图 4-6 所示。

image 2025 01 21 12 37 08 447
Figure 2. 图4-6 Jinja2 for循环

如果被循环的序列(如以上代码中的 books)中没有元素,那么可以使用 else 来处理。通常我们在浏览网页时,如果某个网页没有数据,则会显示 “无数据”。我们在以上代码中的 for.html 模板加上 else,来实现一个类似的需求,代码如下。

...
{% for book in books %}
  <tr>
    <td>{{ book.name }}</td>
    <td>{{ book.author }}</td>
    <td>{{ book.price }}</td>
  </tr>
{% else %}
  <tr>
    <td colspan="3" style="text-align: center;">无数据</td>
  </tr>
{% endfor %}
...

在 books 中无数据的情况下,会执行到 else 中,可以将 for_statement 视图函数的 books 修改为一个空的列表,代码如下。

@app.route("/for")
def for_statement():
    books = []
    return render_template("for.html", books=books)

此时再在浏览器中访问 http://127.0.0.1:5000/for ,可以看到页面会显示 “无数据”,如图 4-7 所示。

image 2025 01 21 12 40 54 222
Figure 3. 图4-7 显示页面无数据

Jinja2 中的 for 循环中还内置了许多好用的变量。如要获取当前循环到第几次了,可以通过 loop.index 来实现。我们还是以 for_statement 视图函数为例,首先将 books 变量恢复成以下形式。

books = [
    {
        "name": "三国演义",
        "author": "罗贯中",
        "price": 100
    },
    {
        "name": "水浒传",
        "author": "施耐庵",
        "price": 99
    },
    {
        "name": "红楼梦",
        "author": "曹雪芹",
        "price": 101
    },
    {
        "name": "西游记",
        "author": "吴承恩",
        "price": 102
    }
]

然后在 for.html 模板中给图书表格新增一个名为序号的列,用 loop.index 来显示每行的序号,修改后的 for.html 模板中的 table 代码如下。

<table>
  <thead>
    <tr>
      <th>序号</th>
      <th>书名</th>
      <th>作者</th>
      <th>价格</th>
    </tr>
  </thead>
  <tbody>
    {% for book in books %}
      <tr>
        <td>{{ loop.index }}</td>
        <td>{{ book.name }}</td>
        <td>{{ book.author }}</td>
        <td>{{ book.price }}</td>
      </tr>
    {% else %}
      <tr>
        <td colspan="4" style="text-align: center;">无数据</td>
      </tr>
    {% endfor %}
  </tbody>
</table>

通过以上代码可以看到,在 thead 标签中新增了一个叫作序号的表头,tbody 中新增了一个叫作 loop.index 列,loop.index 在每次循环时,会显示当前循环的次数,即代表第几行。在浏览器中访问 http://127.0.0.1:5000/for ,可以看到如图 4-8 所示的效果图。

image 2025 01 21 12 49 29 830
Figure 4. 图4-8 带有序号的循环

除 loop.index 外,Jinja2 中的 for 循环中还提供了如表 4-2 所示的变量。

image 2025 01 21 12 51 53 381
Figure 5. 表4-2 for循环中的变量

表4-2 中的 13 个变量中,只有 loop.cycle 和 loop.changed 是函数,其余都是变量。这里再做两个案例来讲解 loop.cycle 和 loop.changed 的用法。

  • loop.cycle:假设现在有一个需求,需要针对 table 标签中行号为奇数的 tr 标签添加 odd 类,行号为偶数的 tr 标签添加 even 类,可以通过以下代码实现。

{% for book in books %}
  <tr class="{{ loop.cycle('odd', 'even') }}">
    <td>{{ loop.index }}</td>
    <td>{{ book.name }}</td>
    <td>{{ book.author }}</td>
    <td>{{ book.price }}</td>
  </tr>
{% endfor %}

在循环 books 的过程中,loop.cycle 也会不断地在 odd 和 even 两个变量中循环,从而实现奇数使用 odd 类,偶数使用 even 类。

  • loop.changed:假设现在想要知道当前循环的 book.name 是否和上次循环的一致,可以通过 loop.changed 实现,代码如下。

{% for book in books %}
  <tr>
    <td>{{ loop.index }}</td>
    <td>{{ book.name }}</td>
    <td>{{ book.author }}</td>
    <td>{{ book.price }}</td>
    <td>{{ loop.changed(book.name) }}</td>
  </tr>
{% endfor %}

Jinja2 模板的 for 循环不存在 break 和 continue 来中断循环的语句,这一点是和 Python 中的 for 循环最大的区别,另外 Jinja2 中只有 for 循环,不存在 while 循环,读者在使用时尤其要注意这两点。