BlackA,Talk is cheap,Show me the code.
到目前为止,我们已经学会了Jinja2模板引擎的基本用法,知道如何定义一个模板,如何向模板传递参数,如何在模板中访问字典和对象,以及如何在模板中使用 if 和 for 语句,你甚至可能已经用Jinja2生成了一些较为复杂网页。接下来,我们将讨论Jinja2的一些高级用法,使用他们,可以节省下你很多的时间。
我们已经知道,在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官方文档 。
模板继承是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官方文档***