模板的基本使用

渲染模板

在使用 PyCharm Professional 版创建完一个 Flask 项目后,默认会生成一个 templates 文件夹,如果没有修改模板查找路径,默认会在这个文件夹下寻找模板文件。模板文件可以是任意纯文本格式的文件,如 TXT、HTML、XML 等,但是为了让项目更规范,也为了与前端开发者更无缝地协作,一般都是用 HTML 文件来写模板代码。

如果读者用的是非 PyCharm Professional 版创建的 Flask 项目,则可以手动创建 templates 文件夹。

首先在 templates 文件夹下创建 index.html 文件,然后输入以下代码。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>首页</title>
  </head>
  <body>
    <h1>这是首页</h1>
  </body>
</html>

接下来在视图函数中使用 render_template 函数渲染 index.html 模板。在 app.py 中,将原来的 hello_world 视图函数修改为以下代码。

from flask import Flask,render_template
    ...
    @app.route('/')
    def index():
        return render_template("index.html")

render_template 默认会从当前项目的 templates 文件夹下寻找 index.html 文件,读取后进行解析,再渲染成 HTML 代码返回给浏览器。在浏览器中访问 http://127.0.0.1:5000 ,可以看到如图 4-1 所示的效果。

image 2025 01 21 11 30 39 538
Figure 1. 图4-1 首页渲染模板代码

从图 4-1 中可以看到,“这是首页” 4 个字已经是一级标题了,原因是模板中给 “这是首页” 4 个字外面套了一个 h1 标签,至此我们就完成了一个最简单的模板渲染。

如果想修改模板文件的查找地址,可以在创建 app 时,给 Flask 类传递一个关键字参数 template_folder 指定具体路径,示例代码如下。

如此操作以后,Flask 在寻找模板文件时,就不再从当前项目下的 templates 文件夹寻找了,而是从 template_folder 指定的路径寻找。项目在 Debug 模式开启的前提下再访问 http://127.0.0.1:5000 ,会出现如图 4-2 所示的错误。

image 2025 01 21 11 31 58 988
Figure 2. 图4-2 模板没有找到

模板没有找到的原因是,在 template_folder 指定的文件夹下不存在一个叫作 index.html 的模板,如果想要解决此问题,只需要把 templates 文件夹下的 index.html 复制到 template_folder 指定的文件夹下即可。

渲染变量

HTML 文件中的有些数据是需要动态地从数据库中加载的,不能直接在 HTML 中写死。一般的做法是,在视图函数中把数据先提取好,然后使用 render_template 渲染模板时传给模板,模板再读取并渲染出来。下面新建一个 URL 与视图函数映射,示例代码如下。

@app.route("/variable")
def variable():
    hobby = "游戏"
    return render_template("variable.html", hobby=hobby)

以上代码中渲染了一个 variable.html 模板,这个模板文件的创建接下来会具体讲解。除模板名称外,还给 render_template 传递了一个 hobby 关键字参数,后续在模板中就可以使用这个变量了。

现在再在 templates 文件夹下创建一个 variable.html 模板文件(注意:要记得先删掉 template_folder 参数),然后输入以下代码。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>变量使用</title>
  </head>
  <body>
    <h1>我的兴趣爱好是:{{ hobby }}</h1>
  </body>
</html>

从以上代码中可以看到,把变量放到两对花括号中即可使用变量。项目运行起来后,在浏览器中访问 http://127.0.0.1:5000/variable ,效果如图 4-3 所示。

image 2025 01 21 11 35 18 945
Figure 3. 图4-3 变量使用

图4-3 中的文字 “游戏” 是从视图函数中通过 render_template 传过去的,并不是在 HTML 中写死的,所以变量的使用可以让同一个 HTML 模板渲染无数个不同的页面。

字典的键和对象的属性在模板中都可以通过点(.)的形式访问。在 variable 这个视图函数中添加两个新的变量,分别是字典类型的 person,以及类对象类型的 user。示例代码如下。

class User:
    def __init__(self, username, email):
        self.username = username
        self.email = email

@app.route("/variable")
def variable():
    hobby = "游戏"
    person = {
        "name": "张三",
        "age": 18
    }
    user = User("李四", "xx@qq.com")
    return render_template("variable.html", hobby=hobby, person=person, user=user)

接下来,再在 variable.html 模板中通过点(.)的形式访问 person 的键和 user 属性。代码如下。

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>变量使用</title>
  </head>
  <body>
    <h1>我的兴趣爱好是:{{ hobby }}</h1>
    <p>person的姓名是:{{ person.name }},person的年龄是:{{ person.age }}</p>
    <p>user的用户名是:{{ user.username }},user的邮箱是:{{ user.email }}</p>
  </body>
</html>

在浏览器中重新访问 http://127.0.0.1:5000/variable ,效果如图 4-4 所示。

字典键和对象的属性也都可以通过中括号的形式获取,如以下代码实际上是等价的。

{{ user.name }}
{{ user["name"] }}

读者可以自行修改 variable.html 中获取键和属性值的方式,最终效果是一样的。用点和中括号的形式访问,虽然效果一样,但是也存在以下不同。

image 2025 01 21 11 43 49 368
Figure 4. 图4-4 模板中通过点渲染字典和对象

(1)在模板中有一个变量的使用方式为 foo.bar,那么在 Jinja2 中则按以下方式进行访问。

  • 通过 getattr(foo, 'bar') 访问,先访问这个对象的属性。

  • 如果没有找到,就通过 foo.__getitem__("bar") 方式访问,即访问这个对象的键。

  • 如果以上两种方式都没有找到,返回一个 undefined 对象。

(2)在模板中有一个变量的使用方式为 foo["bar"],那么在 Jinja2 中则按以下方式进行访问。

  • 通过 foo.__getitem__("bar") 方式访问,即先访问这个对象的键。

  • 如果没有找到,就通过 getattr(foo, "bar") 方式访问,即访问这个对象的属性。

  • 如果以上都没找到,则返回一个 undefined 对象。

以上案例中,传递了 3 个变量到模板中,在变量比较多的情况,首先可以把所有的变量存放到字典中,然后在给 render_template 传递参数时使用 ** 语法,将字典变成关键字参数,以上的 variable 视图函数代码可以改写为以下形式。

@app.route("/variable")
def variable():
    hobby = "游戏"
    person = {
        "name": "张三",
        "age": 18
    }
    user = User("李四", "xx@qq.com")
    context = {
        "hobby": hobby,
        "person": person,
        "user": user
    }
    return render_template("variable.html", **context)

以上代码的写法更加直观和简洁,在遇到需要传给模板的变量比较多的情况,都推荐使用这种方式。