天天看点

Flask JInja2

Jinja2

在动态Web程序中,视图函数返回的HTML数据往往需要根据相应的变量(比如查询参数)动态生成。当HTML代码保存到单独的文件中时,我们没法再使用字符串格式化或拼接字符串的方式来在HTML代码中插入变量,这时我们需要使用模板引擎(template engine)。借助模板引擎,我们可以在HTML文件中使用特殊的语法来标记出变量,这类包含固定内容和动态部分的可重用文件称为模板(template)。

模板引擎的作用就是读取并执行模板中的特殊语法标记,并根据传入的数据将变量替换为实际值,输出最终的HTML页面,这个过程被称为渲染(rendering)。Flask默认使用的模板引擎是Jinja2,它是一个功能齐全的Python模板引擎,除了设置变量,还允许我们在模板中添加if判断,执行for迭代,调用函数等,以各种方式控制模板的输出。

render_template()

Flask 提供的 render_template() 函数把Jinja2模板引擎集成到了应用中。这个函数的第一个参数是模板的文件名,随后的参数都是键 – 值对,表示模板中变量对应的具体值。

from flask import Flask, render_template

@app.route('/')
def index():
    return render_template('index.html')
    
@app.route('/user/<name>')
def user(name):
    return render_template('user.html', name=name)
           

语法

在模板中添加Python语句和表达式时,我们需要使用特定的定界符把它们标示出来。

  1. 语句 比如if判断、for循环 宏等:{% … %}
  2. 表达式 比如字符串、变量、函数调用等:{{ … }}
  3. 注释 {# … #}
{% if user.bio %}
    <i>{{ user.bio }}</i>
{% else %}
    <i>This user has not provided a bio.</i>
{% endif %}

<ul>
    {% for movie in movies %}
        <li>{{ movie.name }} - {{ movie.year }}</li>
    {% endfor %}
</ul>

# Jinja2 还支持宏。宏类似于Python代码中的函数。例如:macro.html
{% macro render_comment(comment) %}
    <li>{{ comment }}</li>
{% endmacro %}

<ul>
    {% for comment in comments %}
        {{ render_comment(comment) }}
    {% endfor %}
</ul>
           

Jinja2允许你在模板中使用大部分Python对象,比如字符串、列表、字典、元组、整型、浮点型、布尔值。它支持基本的运算符号(+、-、*、/等)、比较符号(比如==、!=等)、逻辑符号(and、or、not和括号)以及in、is、None和布尔值(True、False)。

变量的值可以使用过滤器修改。过滤器添加在变量名之后,二者之间以竖线分隔。{{ name|capitalize }}

过滤器名 说明
safe 渲染值时不转义
capitalize 把值的首字母转换成大写,其他字母转换成小写
lower 把值转换成小写形式
upper 把值转换成大写形式
title 把值中每个单词的首字母都转换成大写
trim 把值的首尾空格删掉
striptags 渲染之前把值中所有的 HTML 标签都删掉
first/last 返回第一个、最后一个元素
max/min/unique 返回最大/最小/唯一值
length/wordcount 返回长度、字符统计
default(s) 设置默认值
truncate(s,length=255,killwords=False,end="…") 截断字符

代码复用

为了重复使用宏,可以把宏保存在单独的文件中然后在需要使用的模板中导入:

{% import 'macros.html' as macros %}

<ul>
    {% for comment in comments %}
        {{ macros.render_comment(comment) }}
    {% endfor %}
</ul>
           

多处重复使用的模板代码片段可以写入单独的文件再引入所有模板中,以避免重复:

{% include 'common.html' %}
           

另一种重复使用代码的强大方式是模板继承。

Base.html
<html>
    <head>
        {% block head %}
        <title>{% block title %}{% endblock %} - My Application</title>
        {% endblock %}
    </head>
    <body>
        {% block body %}
        {% endblock %}
    </body>
</html>
           

基模板中定义的区块可在衍生模板中覆盖。Jinja2 使用 block 和 endblock 指令在基模板中定义内容区块。

{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    <style>
    </style>
{% endblock %}
{% block body %}
    <h1>Hello, World!</h1>
{% endblock %}
           

extends 指令声明这个模板衍生自base.html。在extends指令之后,基模板中的3个区块被重新定义,模板引擎会将其插入适当的位置。

如果基模板和衍生模板中的同名区块中都有内容,衍生模板中的内容将显示出来。

在衍生模板的区块里可以调用super(),引用基模板中同名区块里的内容进行追加(head)。

消息闪现

Flask提供了一个非常有用的flash()函数,它可以用来“闪现”需要显示给用户的消息,比如当用户登录成功后显示“欢迎回来!”。在视图函数调用flash()函数,传入消息内容即可“闪现”一条消息。当然,它并不是我们想象的,能够立刻在用户的浏览器弹出一条消息。实际上,使用功能flash()函数发送的消息会存储在session中,我们需要在模板中使用全局函数get_flashed_messages()获取消息并将其显示出来。

from flask import Flask, render_template, flash
app = Flask(__name__)
app.secret_key = 'secret string'

@app.route('/flash')
def just_flash():
    flash('I am flash, who is looking for me?')
    return redirect(url_for('index'))
           

Jinja2内部使用Unicode,所以你需要向模板传递Unicode对象或只包含ASCII字符的字符串。在Python2.x中,如果字符串包含中文(或任何非ASCII字符),那么需要在字符串前添加u前缀,这会告诉Python把这个字符串编码成Unicode字符串,另外还需要在Python文件的首行添加编码声明,这会让Python使用UTF-8来解码字符串,后面不再提示。发送中文消息的示例如下所示:

# -*- coding: utf-8 -*-

@app.route('/flash')
def just_flash():
    flash(u'你好,我是闪电。')
    return redirect(url_for('index'))
           

使用Flask-Moment本地化日期和时间

# 主程序初始化
from flask_moment import Moment
moment = Moment(app)

# 模板里引入 Moment.js 库
{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{% endblock %}

# 应用视图添加datetime对象
from datetime import datetime

@app.route('/')
def index():
    return render_template('index.html',current_time=datetime.utcnow())
    
# 使用 Flask-Moment 渲染时间戳
<p>The local date and time is {{ moment(current_time).format('LLL') }}.</p>
<p>That was {{ moment(current_time).fromNow(refresh=True) }}</p>
           

format(‘LLL’)函数根据客户端计算机中的时区和区域设置渲染日期和时间。参数决定了渲染的方式,从 ‘L’ 到 ‘LLLL’ 分别对应不同的复杂度。format()函数还可接受很多自定义的格式说明符。

Token Output
Month
M 1 2 … 11 12
Mo 1st 2nd … 11th 12th
MM 01 02 … 11 12
MMM Jan Feb … Nov Dec
MMMM January February … November December
Quarter
Q 1 2 3 4
Qo 1st 2nd 3rd 4th
Day of Month
D 1 2 … 30 31
Do 1st 2nd … 30th 31st
DD 01 02 … 30 31
Day of Year
DDD 1 2 … 364 365
DDDo 1st 2nd … 364th 365th
DDDD 001 002 … 364 365
Day of Week
d 0 1 … 5 6
do 0th 1st … 5th 6th
dd Su Mo … Fr Sa
ddd Sun Mon … Fri Sat
dddd Sunday Monday … Friday Saturday
Day of Week (Locale)
e 0 1 … 5 6
Day of Week (ISO)
E 1 2 … 6 7
Week of Year
w 1 2 … 52 53
wo 1st 2nd … 52nd 53rd
ww 01 02 … 52 53
Week of Year (ISO)
W 1 2 … 52 53
Wo 1st 2nd … 52nd 53rd
WW 01 02 … 52 53
Year
YY 70 71 … 29 30
YYYY 1970 1971 … 2029 2030
Y 1970 1971 … 9999 +10000 +10001
Week Year
gg 70 71 … 29 30
gggg 1970 1971 … 2029 2030
Week Year (ISO)
GG 70 71 … 29 30
GGGG 1970 1971 … 2029 2030
AM/PM
A AM PM
a am pm
Hour
H 0 1 … 22 23
HH 00 01 … 22 23
h 1 2 … 11 12
hh 01 02 … 11 12
k 1 2 … 23 24
kk 01 02 … 23 24
Minute
m 0 1 … 58 59
mm 00 01 … 58 59
Second
s 0 1 … 58 59
ss 00 01 … 58 59
说明 简写 实例
Time LT 8:30 PM
Time with seconds LTS 8:30:25 PM
Month numeral, day of month, year L 09/04/1986
同上 l 9/4/1986
Month name, day of month, year LL September 4, 1986 ll Sep 4, 1986
Month name, day of month, year, time LLL September 4, 1986 8:30 PM
同上 lll Sep 4, 1986 8:30 PM
Month name, day of month, day of week, year, time LLLL Thursday, September 4, 1986 8:30 PM
同上 llll Thu, Sep 4, 1986 8:30 PM

第二行中的 fromNow()渲染相对时间戳,而且会随着时间的推移自动刷新显示的时间。这个时间戳最开始显示为“a few seconds ago”,但设定refresh=True参数后,其内容会随着时间的推移而更新。如果一直待在这个页面,几分钟后会看到显示的文本变成“a minute ago”“2minutes ago”,等等。

Flask-Moment 渲染的时间戳可实现多种语言的本地化。语言可在模板中选择,方法是在引入Moment.js之后,立即把两个字母的语言代码传给locale()函数。例如,配置 Moment.js 使用西班牙语的方式如下:

{% block scripts %}
{{ super() }}
{{ moment.include_moment() }}
{{ moment.locale('es') }}
{% endblock %}