Jinjia2是Flask使用的html渲染模板,Jinjia原意为日本的神社,英文为temple,与模板的英文template相近,故取名jinjia。
首先先演示如何在flask代码中展示静态的html,方法是使用flask库中的render_template()方法,其参数为template(默认为template,也可以通过参数修改)文件夹中的“index.html”,通过这种方式,可以在进入指定UML之后,访问html页面。
@app.route('/')
def hello_world(): # put application's code here
return render_template("index.html")

👇index.html的html页面代码:
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>首页title>
head>
<body>
<h1>这是首页h1>
body>
html>

HTML中有些数据需要动态地从数据库中加载,不能直接写死在HTML页面中,在视图函数中可以将数据提取好,再通过Jinjia2传递参数的方式将数据在视图函数中通过render_template()(render的本意即为渲染)传递给HTML。在html中使用双花括号{{ hobby }}即可使用变量,此处的hobby即为在视图函数中使用render_template传递过来的变量。此外,字典的键值和对象的属性在模板中都可以通过点(.)来传递, 还可以通过["属性名"]的方式传递, 二者的区别在于, 使用.先查看变量是否为字典, 再查看是否为对象, 都不是则返回undefined对象, 而使用方括号则字典和对象的寻找顺序相反.
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)
👆注意, 如果传递参数时, 名字出错了, 则在html页面不会显示, 推测是因为没有打开debug模式. 👇
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>

此外, 还可以通过对字典使用**运算,使字典变成关键字参数. 其效果与上面的代码等同.
@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)
差点忘了Markdown是可以开二级标题的…
向html中传递参数时, 可能需要对传递的变量进行处理, 处理过后再显示, 如时间的格式化输出等, 此时可以用到过滤器. Jinjia2内置了许多过滤器, 但过滤器可以由用户进行自定义. 因为过滤器本质上是一个函数.
在定义了一个函数之后, 可以使用app.add_template_filter(datetime_format, "dformat")方法将函数添加为过滤器, 其中datetime_format为函数, 而dformat为过滤器名.
def datetime_format(value, format="%Y-%d-%m %H: %M"):
return value.strftime(format)
app.add_template_filter(datetime_format, "dformat")
除此之外, 还可以通过装饰器的方式定义过滤器.
@app.template_filter("dformat")
def datetime_format(value, format="%Y-%d-%m %H: %M"):
return value.strftime(format)
可以看到, 使用的仍然是带参数的装饰器, 参数列表为过滤器的名称 ( 如果不加参数, 则默认将函数名当作过滤器的名称 ). 两种声明方法是等价的.
详情可查看官方文档.
用来测试某些元素是否满足条件, 如测试一个变量是否为字符串, 测试一个变量能否被调用(类似于iscallable())等.
{% if user is defined %}
user定义了:{{ user }}
{% else %}
user没用定义。
{% endif %}



Jinjia2中的if判断句与python中的if判断句非常相似, 甚至比较运算符和逻辑运算符都完全相同, 并且也使用elif作为else if. 注意, 当if语句块结束时, 需要声明endif.
{% if age > 18 %}
<div>您已成年!div>
{% elif age < 18 %}
<div>您未成年!div>
{% else %}
<div>您刚成年!div>
{% endif %}
👆Jinjia2中代码缩进的目的是更加方便阅读, 实际上任何代码缩进在Jinjia2中都是不必要的.

Jinjia2的for循环语句只是比python多加了一个endfor声明.
@app.route('/for')
def for_statement():
books = [{
"name":"三国演义",
"author":"罗贯中",
"price":100
},{
"name":"水浒传",
"author":"施耐庵",
"price":101
},{
"name":"西游记",
"author":"吴承恩",
"price":99
},{
"name":"红楼梦",
"author":"曹雪芹",
"price":88
},
]
return render_template("for.html",books=books)
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>
{% else %}
<tr>
<td colspan="3" style="text-align: center;">无数据td>
tr>
{% endfor %}
tbody>
table>
body>
html>
👆如果被循环的列表books中没有元素, 那么可以定义else语句块, 显示"无数据".


此外, Jinjia2的for循环中还内置了许多有用的变量, 如获取当前循环到了第几次, 可以使用loop.index来查看.
{% 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="3" style="text-align: center;">无数据td>
tr>
{% endfor %}
👆添加loop.index



模板中的宏(macro)与python中的函数非常相似, 可以传递参数但是没有返回值. 可以将一些常用的代码片段放到宏中, 然后把一些不固定的值抽取出来当成一个参数.
👇将宏定义在一个单独的文件forms.html中.
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>formstitle>
head>
<body>
{% macro input(name, value='', type='text') %}
<input type="{{ type }}" value="{{ value|escape }}" name="{{ name }}">
{% endmacro %}
{% macro textarea(name, value='', rows=10, cols=40) %}
<textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols }}">{{ value|escape }}textarea>
{% endmacro %}
body>
html>
👆可以看到, 通过macro标签创建了一个名为input的宏, 它接收参数name和type.
👇导入宏, 使用import, 规则和python的import相同(支持import…as…和from…import…as…等语法). 加上with context, 可以导入当前模板中的变量.
<!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>
<p>
{% if user is defined %}
user定义了:{{ user }}
{% else %}
user没用定义。
{% endif %}
</p>
{% import 'forms.html' as forms with context %}
<dl>
<dt>Username</dt>
<dd>{{ forms.input('username') }}</dd>
<dt>Password</dt>
<dd>{{ forms.input('password', type='password') }}</dd>
</dl>
<p>{{ forms.textarea('comment') }}</p>
</body>
</html>

通过模板继承, 把一些重复性的代码写在父模板当中, 子模板继承父模板后, 再分别实现自己的代码.

Jinjia2中的模板上下文即为在整个模板中都可以使用的变量, 可以通过app.context_processor进行声明. 此外, 还可以通过set方法进行创建. 使用set方法后, 创建的变量在这一句之后都是有效的.
{% set name = 'admin' %}
如果不想让这个变量污染全局环境, 还可以用with语句块指定作用域.
{% with %}
{% set foo = 42 %}
{{ foo }}
{% end with %}
也可以直接在with语句块内声明该作用域的变量:
{% with foo = 42 %}
{{foo}}
{% end with %}
👆以上两种方式等价.
此外, Jinjia2中还有内置的全局变量, 如g, request, session和config.
Jinjia2虽然内置了一些全局变量, 但有时候仍然需要传递自定义的变量, 如很多网站的导航条右上角会显示当前登录的用户名, 这需要把username传递到几乎所有的模板, 定义一个全局变量很合适. 仍然是使用装饰器.
@app.context_processor
def context_user():
user = {"username":"admin", "level":2}
return {"user":user}
👆在自定义的上下文处理器函数中, 需要把变量放到字典中才能被函数所使用, 正如上述代码块所定义的.
url_for( 用于加载静态文件, 或者用于构建URL )以get_flashed_message( 用于获取闪现消息 ).@app.template_global()@app.template_global()
def greet(name):
return "欢迎! %s" % name
使用:
<div>{{ greet("詹森") }}</div>
使用闪现消息, 需要在视图函数中通过flash()提交要传递的消息, 然后再在模板中使用get_flashed_messages函数获取视图函数中的消息内容. 由于get_flashed_messages返回一个列表, 因此需要通过下标或者for循环来读取闪现消息.
Noteworthy的是, 闪现消息存储在session中, 使用session之前需要在app.config中设置SECRET_KEY, 幸运的是SECRET_KEY可以设置为任意字符串.
app.config['SECRET_KEY'] = "ewgnlew&S;12zd-+"
@app.route('/flash')
def myflash():
flash("闪现消息1...")
flash("闪现消息2...")
return render_template("flash.html")
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>flashtitle>
head>
<body>
<ul>
{% for message in get_flashed_messages() %}
<li>{{ message }}li>
{% endfor %}
ul>
body>
html>
