专否 写文章

BlackA,Talk is cheap,Show me the code.

Nov 3, 2018
Follow

Flask 学习笔记 #6 -- Jinja2模板 III

到目前为止,我们已经学会了Jinja2模板引擎的基本用法,知道如何定义一个模板,如何向模板传递参数,如何在模板中访问字典和对象,以及如何在模板中使用 if 和 for 语句,你甚至可能已经用Jinja2生成了一些较为复杂网页。接下来,我们将讨论Jinja2的一些高级用法,使用他们,可以节省下你很多的时间。

1、过滤器

我们已经知道,在Jinja2模板中,由 ’ {{ ‘ 和 ’ }} ‘ 扩起的部分表示要传入的变量,变量会在渲染模板时被实际传入的参数值替换,从而动态的生成网页。然而实际传入的参数值可能与我们的预期不符(因为我们无法预测用户的行为),这时就需要对变量值进行转换处理。

过滤器就是在变量被显示或使用前,对其作转换处理的工具。可以把它认为是一种转换函数,函数的参数是其所修饰的变量,函数的返回值是转换后的值。过滤器与变量间用管道符号 ’ | ‘ 分割,并且可以用圆括号传递可选参数。多个过滤器间可以链式调用,前一个过滤器的输出会被作为后一个过滤器的输入。

我们来看一个例子,这次我们仿照的网站是专否,当用户没有设置头像时,显示默认的头像,当用户设置了头像时,显示用户设置的头像:

首先我们需要把这两张头像图片下载下来,并保存到项目根目录的static文件夹下(Flask框架静态文件的默认存放位置):

E:.
|   main.py
|
+---static
|       default.png
|       user.jpg
|
\---templates
        index.html

下面是Flask程序的代码:

from flask import Flask,render_template,url_for
app = Flask(__name__)

# is_set的值为1时表示用户已经设置了头像,为0时表示没有设置头像
@app.route('/<is_set>/')
def index(is_set):

    # 如果用户设置了头像就把头像的url传递给模板文件,这里使用url_for()函数动态的构建url
    # 使用url_for()构建静态文件的url时,第一个参数为"static",
    # 并且使用关键字参数filename传递文件的相对路径
    if is_set == '1':
        return render_template("index.html",
                                avatar = url_for("static",filename = "user.jpg"))
    # 如果用户没有设置头像就什么也不传递
    else:
        return render_template("index.html")

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

在上面这个程序中,如果用户的请求的为 ‘ 1 ’,表示用户设置了头像,则把头像图片的url传递给模板,如果请求为 ‘ 0 ’ ,表示没有设置头像,则什么也不传递,这就需要我们的模板可以在没有接受到avatar参数时给avatar变量设定一个默认值,这就可以通过default过滤器实现:

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8">
    <title>show avatar</title>
</head>
<body>
    {# 当没有接受到avatar参数时通过default过滤器为avatar设置默认值 #}
    {# 通过在模板中使用url_for()函数实现动态的构建图片的路径 #}
    <img src={{ avatar|default(url_for("static",filename = "default.png")) }} \>
</body>
</html>

下面是这段代码的运行结果:

下面给出了一些常用的Jinja2内置过滤器,如需其他的可以参考官方文档

# abs            返回一个数值的绝对值
# default        如果当前变量没有值,则会使用参数中的值来代替
# first          返回一个序列的第一个元素
# format         格式化字符串
# length         返回一个序列或者字典的长度
# int            将值转换为整数类型
# float          将值转换为浮点数类型
# lower          将字符串转换为小写
# upper          将字符串转换为大写
# string         将变量转换成字符串

另外,Flak还支持用户把函数自定义为过滤器,这里不再讨论,如有需要可参考Flask官方文档 。

2、模板继承

模板继承是Jinja2中一个非常有用的功能,这个功能继承允许你创建一个基础的骨架模板(父模板), 这个模板包含网站的通用元素,并且定义子模板可以重载的代码块,然后在子模板中实现这些代码块。通过模板继承,Jinja2可以像任何面向对象语言一样,实现模板代码的复用,大大的提高开发效率。

我们接下来看一个例子:打开专否的首页,观察首页的导航条,我们可以看到这个导航条由专否的图标和一系列按钮构成,接下来我们随便点开一个问题,发现在问题的上方也有一个完全一样的导航条(我们这里不考虑由CSS控制的外观,只考虑导航条的内容),也就是说这个导航条是首页和问题页面的公共部分,我们可以把这个公共的导航条抽象到一个父模板中,然后在每一个页面文件中继承这个父模板,从而实现代码的复用。

我们先来写Flask程序的代码:

from flask import Flask,render_template
app = Flask(__name__)

# 首页
@app.route('/')
def index():
    return render_template("index.html")

# 问题页面
@app.route("/question/")
def question():
    return render_template("question.html")

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

我们这个程序和以前的没有区别,当用户访问网站时将显示首页,当用户访问questions时将显示问题页面。下面是父模板的代码(base.html):

<!DOCTYPE html>
<html lang="zh-cn">
<head>
    <meta charset="utf-8" />

    {# 父模板中需要子模板重载的内容由bolck语句块括起来 #}
    <title>{% block title %}{% endblock %}</title>

</head>
<body>
    <header>
        <p>开始导航栏部分</p>
        <nav>
            <ul>
                {# 这里使用url_for()函数得到对应的路径 #}
                <li><a href="{{ url_for('index') }}">主页</a></li>
                <li><a href="{{ url_for('question') }}">问题</a></li>
            </ul>
        </nav>
        <p>结束导航栏部分</p>
    </header>
    {# 父模板的block语句块中也可以有自己的代码,并由子模板决定是否要继承 #}
    {% block main %}
        <h1>这是父模板中的代码</h1>
    {% endblock %}
</body>
</html>

我们把导航栏的内容(公共部分)放在父模板中,并在父模板中定义希望被子模板重载的语句块(标题和主要内容),需要重载的语句块由 ‘ block ’ 和 ‘ endblock ‘ 括起来,block后面紧跟这语句块的名字,而且block语句块中可以有自己的代码,该代码将会由子模板决定是否被继承。之后我们来完成首页(index.html):

{# 使用extends语句表示继承base.html文件中的内容 #}
{% extends "base.html" %}

{# 重载的语句块(网页标题部分) #}
{% block title %}首页{% endblock %}

{# 重载的语句块(主题内容部分) #}
{% block main %}
    {# super()函数表示子模板继承父模板中的block的内容,这里将显示父模板中block语句块的内容 #}
    {{ super() }}
    <h1>这是首页页面!</h1>
{% endblock %}

在子模板中使用extends语句(extends语句后面紧跟父模板名)表示要继承哪个父模板的内容,block语句块为重载后的代码,也就是说会用子模板block中的内容替换父模板中同名block语句块中的内容,并且可以在子模板中使用super()语句在替换时保留父模板block语句块中的内容。

最后是问题页面(question.html):

{% extends "base.html" %}

{% block title %}问题{% endblock %}

{% block main %}
    {# 这里我们没有使用super()函数,所以父模板中block语句块的的内容不会显示 #}
    <h1>这是问题页面!</h1>
{% endblock %}

下面是运行示例:



*** 注: 到这里为止,我们已经学完了Jinja2模板的常用功能,但是Jinja2模板是一套非常强大的模板引擎,限于篇幅,很多问题无法在这里一一讨论,如有需要请查阅 Jinja2官方文档***

喜欢这个文章 | 分享 | 新建跟帖