模板是一個包含響應(yīng)文本的文件,其中包含用占位變量表示的動態(tài)部分,其具體值只在請求的上下文中才能知道。使用真實值替換變量,再返回最終得到的響應(yīng)字符串,這一過程稱為渲染。為了渲染模板,F(xiàn)lask使用了一個名為Jinja2的強大模板引擎。
4.1 Jinja2模板引擎
模板可以生成任何基于文本的格式(HTML、XML、CSV、LaTex 等等)。它并沒有特定的擴展名, .html或 .xml都是可以的。
模板包含 變量 或 表達式 ,這兩者在模板求值的時候會被替換為值。模板中 還有標簽,控制模板的邏輯。模板語法的大量靈感來自于 Django 和 Python 。
下面是一個最小的模板,它闡明了一些基礎(chǔ)。我們會在文檔中后面的部分解釋細節(jié):
<!DOCTYPE HTML>
<html lang="zh">
<head>
<title>藕絲空間歡迎您! </title>
</head>
<body>
<ul id="navigation">
{% for item in navigation %}
<li><a href="{{ item.href }}">{{ item.caption }}</a></li>
{% endfor %}
</ul>
<h1>藕絲空間歡迎您! </h1>
{{ a_variable }}
</body>
</html>
這里有兩種分隔符: {% ... %}和 {{ ... }}。前者用于執(zhí)行諸如 for 循環(huán) 或賦值的語句,后者把表達式的結(jié)果打印到模板上。
4.1.1 模板變量
在模板中使用的{{ a }}結(jié)構(gòu)表示一個變量,它是一種特殊的占位符,告訴模板引擎這個位置的值從渲染模板時使用的數(shù)據(jù)中獲取。Jinja2 能識別所有類型的變量,甚至是一些復(fù)雜的類型,例如列表,字典和對象。在模板中使用的變量一些示例如下:
<p> 一個字典變量:{{ mydict['key'] }}。</p>
<p> 一個列表變量:{{ mylist['index'] }}。</p>
<p> 一個列表變量,并且?guī)в兴饕?{{ mylist['myintvar] }}。</p>
<p> 一個對象變量:{{ myobj.somemethod() }}。</p>
4.1.2 過濾器
變量可以通過 過濾器 修改。過濾器與變量用管道符號( |)分割,并且也 可以用圓括號傳遞可選參數(shù)。多個過濾器可以鏈式調(diào)用,前一個過濾器的輸出會被作為 后一個過濾器的輸入。
例如 {{ name|striptags|title }}會移除 name中的所有 HTML 標簽并且改寫 為標題樣式的大小寫格式。過濾器接受帶圓括號的參數(shù),如同函數(shù)調(diào)用。這個例子會把一個列表用逗號連接起來: {{ list|join(', ') }}。
下表列出了 Jinja2 提供的部分常用過濾器
| 過濾器名 | 說明 |
|---|---|
| safe | 渲染值時不轉(zhuǎn)義 |
| capitalize | 把值的首字母轉(zhuǎn)換成大寫,其它字母轉(zhuǎn)換成小寫 |
| lower | 把值轉(zhuǎn)換成小寫形式 |
| upper | 把值轉(zhuǎn)換成大寫形式 |
| title | 把值的每個單詞的首字母都轉(zhuǎn)換為大寫 |
| trim | 把值的首尾空格去掉 |
| striptags | 渲染之前把之中所有的 HTML 標簽都刪除 |
safe 過濾器值要特別說明一下。默認情況下,出于安全考慮,Jinja2 會轉(zhuǎn)義所有變量。例如,如果一個變量值為<h1>Hello</h1>,Jinja2 會將其渲染成<h1>Hello</h1>瀏覽器能顯示這個 h1 元素,但不進行解釋。很多情況下需要顯示變量中存儲的 HTML 代碼,這時就可以使用 safe 過濾器。
4.1.3 注釋
要把模板中一行的部分注釋掉,默認使用 {# ... #} 注釋語法。這在調(diào)試或 添加給你自己或其它模板設(shè)計者的信息時是有用的:
{# note: disabled template because we no longer use this
{% for user in users %}
...
{% endfor %}
#}
4.1.4 空白控制
默認配置中,模板引擎不會對空白做進一步修改,所以每個空白(空格、制表符、換行符 等等)都會原封不動返回。如果應(yīng)用配置了 Jinja 的 trim_blocks,模板標簽后的 第一個換行符會被自動移除(像 PHP 中一樣)。
此外,你也可以手動剝離模板中的空白。當你在塊(比如一個 for 標簽、一段注釋或變 量表達式)的開始或結(jié)束放置一個減號( -),可以移除塊前或塊后的空白:
{% for item in seq -%}
{{ item }}
{%- endfor %}
這會產(chǎn)出中間不帶空白的所有元素。如果 seq是 1到 9的數(shù)字的列表, 輸出會是123456789。
如果開啟了 行語句 ,它們會自動去除行首的空白。
提示:
標簽和減號之間不能有空白。
有效的:
{%- if foo -%}
...
{% endif %}
無效的:
{% - if foo - %}
...
{% endif %}
4.1.5 轉(zhuǎn)義
有時想要或甚至必要讓 Jinja 忽略部分,不會把它作為變量或塊來處理。例如,如果 使用默認語法,你想在在使用把 {{作為原始字符串使用,并且不會開始一個變量 的語法結(jié)構(gòu),你需要使用一個技巧。
最簡單的方法是在變量分隔符中( {{)使用變量表達式輸出:
{{ '{{' }}
對于較大的段落,標記一個塊為 raw是有意義的。例如展示 Jinja 語法的實例, 你可以在模板中用這個片段:
{% raw %}
<ul>
{% for item in seq %}
<li>{{ item }}</li>
{% endfor %}
</ul>
{% endraw %}
4.1.6 行語句
如果應(yīng)用啟用了行語句,就可以把一個行標記為一個語句。例如如果行語句前綴配置為 #,下面的兩個例子是等價的:
<ul>
# for item in seq
<li>{{ item }}</li>
# endfor
</ul>
<ul>
{% for item in seq %}
<li>{{ item }}</li>
{% endfor %}
</ul>
行語句前綴可以出現(xiàn)在一行的任意位置,只要它前面沒有文本。為了語句有更好的可讀 性,在塊的開始(比如 for、 if、 elif等等)以冒號結(jié)尾:
# for item in seq:
...
# endfor
提示:
若有未閉合的圓括號、花括號或方括號,行語句可以跨越多行:
<ul>
# for href, caption in [('index.html', 'Index'),
('about.html', 'About')]:
<li><a href="{{ href }}">{{ caption }}</a></li>
# endfor
</ul>
從 Jinja 2.2 開始,行注釋也可以使用了。例如如果配置 ##為行注釋前綴, 行中所有 ##之后的內(nèi)容(不包括換行符)會被忽略:
# for item in seq:
<li>{{ item }}</li> ## this comment is ignored
# endfor
4.1.7 控制結(jié)構(gòu)
Jinja2提供了多種控制結(jié)構(gòu),可用來改變模板的渲染流程。
條件控制語句:
{% if user %}
你好, {{ user }}!
{% endif %}
循環(huán)控制語句及宏(宏類似于Python代碼中的函數(shù)):
{% macro render_comment(comment) %}
<li>{{ comment }}</li>
{% endmacro %}
<ul>
{% for comment in comments %}
{{ render_comment(comment) }}
{% endfor %}
</ul>
在一個 for 循環(huán)塊中你可以訪問這些特殊的變量:
| 變量 | 描述 |
|---|---|
| loop.index | 當前循環(huán)迭代的次數(shù)(從 1 開始) |
| loop.index0 | 當前循環(huán)迭代的次數(shù)(從 0 開始) |
| loop.revindex | 到循環(huán)結(jié)束需要迭代的次數(shù)(從 1 開始) |
| loop.revindex0 | 到循環(huán)結(jié)束需要迭代的次數(shù)(從 0 開始) |
| loop.first | 如果是第一次迭代,為 True 。 |
| loop.last | 如果是最后一次迭代,為 True 。 |
| loop.length | 序列中的項目數(shù)。 |
| loop.cycle | 在一串序列間期取值的輔助函數(shù)。見下面的解釋。 |
在 for 循環(huán)中,可以使用特殊的 loop.cycle 輔助函數(shù),伴隨循環(huán)在一個字符串/變 量列表中周期取值:
{% for row in rows %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
{% endfor %}
重復(fù)使用宏的方法:
{% import 'macros.html' as macros %}
<ul>
{% for comment in comments %}
{{ macros.render_comment(comment) }}
{% endfor %}
</ul>
4.1.8 模板繼承
類似于Python代碼中的類繼承。首先創(chuàng)建一個名為base.html的基模板:
<html>
<head>
{% block head %}
<title>{% block title %}{% endblock %} - 我的程序</title>
{% endblock %}
</head>
<body>
{% block body %}
{% endblock %}
</body>
</html>
基模板的衍生模板:
{% extends 'base.html' %}
{% block title %}首頁{% endblock %}
{% block head %}
{{ super() }}
<style>
</style>
{% endblock %}
{% block body %}
<h1>你好,世界!</h1>
{% endblock %}
extents指令聲明這個模板衍生自base.html。在extents指令之后,基模板中的3個塊被重新定義,模板引擎會將其插入適當?shù)奈恢?。super()函數(shù)用來獲取基模板原來的內(nèi)容。
4.2 在程序中配置bootstrap模板
要想在程序中集成Bootstrap,顯然要對模板做必要的改動。有一個更簡單的方法是使用一個名為Flask-Bootstrap的Flask擴展,簡化集成過程。
(flask)$ pip install flask-bootstrap
不過由于上述插件功能有限,推薦使用自己配置的Bootstrap模板。
將bootstrap模板中的核心css文件 bootstrap.min.css 和主題css文件bootstrap-theme.mim.css復(fù)制到 靜態(tài)文件夾static 的 static/css文件夾里,同時將字體文件復(fù)制到static/fonts文件夾里;將jquery核心js文件jquery.min.js和bootstrap的js文件bootstrap.min.js文件復(fù)制到static/js文件夾里。同時,你可以將自己的個性化模板所用用到的css和js文件復(fù)制到相應(yīng)的文件夾里。
4.3 渲染模板
默認情況下,Flask 在程序文件夾中的 templates 子文件夾中尋找模板。在下一個hello.py版本中,要把已定義的模板保存在templates文件夾中,并分別命名為index.html和user.html。
示例 4-2 hello.py:渲染模板
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)
Flask提供的render_template函數(shù)把Jinja2模板引擎集成到了程序中。render_template函數(shù)第一個參數(shù)是模板的文件名。隨后的參數(shù)都是鍵值對,表示模板中變量對應(yīng)的真實值。
啟動你的項目:
(flask)$ python hello.py
這時你應(yīng)該可以看到你自己的網(wǎng)頁了。
最后,請發(fā)揮你的想象力,利用bootstrap的前端技術(shù),來充分個性化你的頁面吧。