默認(rèn)情況下,F(xiàn)lask 在程序文件夾中的 templates 子文件夾中尋找模板。在下一個(gè) hello.py 版本中,要把前面定義的模板保存在 templates 文件夾中,并分別命名為 index.html 和 user.html。
index.thm的代碼如下,不傳參數(shù)
<html>
<head>
<title>歡迎來(lái)到王者榮耀</title>
</head>
<body>
<p>你是不是要成為一個(gè)高手</p>
</body>
</html>
user.html中傳遞一個(gè)參數(shù)
<html>
<head>
<title>歡迎來(lái)到王者榮耀</title>
</head>
<body>
<p>你是不是要成為一個(gè)高手,你的名字是{{name}}</p>
</body>
</html>
在hello.py中,
from flask import render_template
@app.route('/')
def index():
return render_template('index.html')
@app.route('/user/<name>') #尖括號(hào)中的動(dòng)態(tài)名字
def user(name):
return render_template('user.html',name=name)
1、變量
在模板中使用的 {{ name }} 結(jié)構(gòu)表示一個(gè)變量,它是一種特殊的占位符,告訴模
板引擎這個(gè)位置的值從渲染模板時(shí)使用的數(shù)據(jù)中獲取。
Jinja2 能識(shí)別所有類型的變量,甚至是一些復(fù)雜的類型,例如列表、字典和對(duì)象。在模板 中使用變量的一些示例如下:
<p>A value from a dictionary: {{ mydict['key'] }}.</p>
<p>A value from a list: {{ mylist[3] }}.</p>
<p>A value from a list, with a variable index: {{ mylist[myintvar] }}.</p>
<p>A value from an object's method: {{ myobj.somemethod() }}.</p>
可以使用過(guò)濾器修改變量,過(guò)濾器名添加在變量名之后,中間使用豎線分隔。例如,下述 模板以首字母大寫形式顯示變量 name 的值:
Hello, {{ name|capitalize }}
下面是常用的過(guò)濾器

更多的fliter官方文檔
2、Jinja2的控制結(jié)構(gòu)
if - else 的寫法
{% if name %}
Hello,{{name}}
{% else %}
Hello ,你沒名字一看就是菜啊!!!
{% endif %}
for 循環(huán)
{% for title in ["黃金4","黃金3","黃金2"] %}
<li>{{title}}</li>
{% endfor %}
Jinja2 還支持宏。宏類似于 Python 代碼中的函數(shù)。例如:
{% macro render_comment(comment) %}
<li>{{ comment }}</li>
{% endmacro %}
<ul>
{% for comment in comments %}
{{ render_comment(comment) }}
{% endfor %}
</ul>
為了重復(fù)使用宏,我們可以將其保存在單獨(dú)的文件中,然后在需要使用的模板中導(dǎo)入:
{% import 'macros.html' as macros %}
<ul>
{% for comment in comments %}
{{ macros.render_comment(comment) }}
{% endfor %}
</ul>
需要在多處重復(fù)使用的模板代碼片段可以寫入單獨(dú)的文件,再包含在所有模板中,以避免 重復(fù):
{% include 'common.html' %}
另一種重復(fù)使用代碼的強(qiáng)大方式是模板繼承,它類似于 Python 代碼中的類繼承。首先,創(chuàng)
建一個(gè)名為 base.html 的基模板:
<html>
<head>
{% block head %}
<title>{% block title %}{% endblock %} - MyApplication</title>
{% endblock %}
</head>
<body>
{%block body %}
<p>我是來(lái)自基類</p>
{%endblock %}
</body>
</html>
新建一個(gè)son.html 繼承base.html
{% extends "base.html" %}
{% block title %} INDEX {% endblock %}
{% block head %}
{{super()}}
{% endblock %}
{% block body %}
{{super()}}
<h1>我也是醉了,這是啥基礎(chǔ)自基礎(chǔ)模板</h1>
{% endblock %}
extends 指令聲明這個(gè)模板衍生自 base.html。在 extends 指令之后,基模板中的 3 個(gè)塊被 重新定義,模板引擎會(huì)將其插入適當(dāng)?shù)奈恢?。注意新定義的 head 塊,在基模板中其內(nèi)容不 是空的,所以使用 super() 獲取原來(lái)的內(nèi)容。
3、自定義錯(cuò)誤頁(yè)面的方式
自定義錯(cuò)誤頁(yè)面
@app.errorhandler(404)
def page_not_found(e):
return render_template('404.html'), 404
@app.errorhandler(500)
def internal_server_error(e):
return render_template('500.html'), 500
4、鏈接
任何具有多個(gè)路由的程序都需要可以連接不同頁(yè)面的鏈接,例如導(dǎo)航條。
在模板中直接編寫簡(jiǎn)單路由的 URL 鏈接不難,但對(duì)于包含可變部分的動(dòng)態(tài)路由,在模板 中構(gòu)建正確的 URL 就很困難。而且,直接編寫 URL 會(huì)對(duì)代碼中定義的路由產(chǎn)生不必要的 依賴關(guān)系。如果重新定義路由,模板中的鏈接可能會(huì)失效。
為了避免這些問(wèn)題,F(xiàn)lask 提供了 url_for() 輔助函數(shù),它可以使用程序 URL 映射中保存 的信息生成 URL。
url_for() 函數(shù)最簡(jiǎn)單的用法是以視圖函數(shù)名(或者 app.add_url_route() 定義路由時(shí)使用 的端點(diǎn)名)作為參數(shù),返回對(duì)應(yīng)的 URL。例如,在當(dāng)前版本的 hello.py程序中調(diào)用 url_ for('index')得到的結(jié)果是/。調(diào)用url_for('index', _external=True)返回的則是絕對(duì)地 址,在這個(gè)示例中是 http://localhost:5000/。
生成連接程序內(nèi)不同路由的鏈接時(shí),使用相對(duì)地址就足夠了。如果要生成在 瀏覽器之外使用的鏈接,則必須使用絕對(duì)地址,例如在電子郵件中發(fā)送的 鏈接。
使用 url_for() 生成動(dòng)態(tài)地址時(shí),將動(dòng)態(tài)部分作為關(guān)鍵字參數(shù)傳入。例如,url_for ('user', name='john', _external=True) 的返回結(jié)果是 http://localhost:5000/user/john。
傳入 url_for() 的關(guān)鍵字參數(shù)不僅限于動(dòng)態(tài)路由中的參數(shù)。函數(shù)能將任何額外參數(shù)添加到 查詢字符串中。例如,url_for('index', page=2) 的返回結(jié)果是 /?page=2。
5、靜態(tài)文件
Web 程序不是僅由 Python 代碼和模板組成。大多數(shù)程序還會(huì)使用靜態(tài)文件,例如 HTML
代碼中引用的圖片、JavaScript 源碼文件和 CSS。
例如,調(diào)用 url_for('static', filename='css/styles.css', _external=True) 得 到 的 結(jié) 果 是 http:// localhost:5000/static/css/styles.css。
默認(rèn)設(shè)置下,F(xiàn)lask 在程序根目錄中名為 static 的子目錄中尋找靜態(tài)文件。如果需要,可在 static 文件夾中使用子文件夾存放文件。服務(wù)器收到前面那個(gè) URL 后,會(huì)生成一個(gè)響應(yīng), 包含文件系統(tǒng)中 static/css/styles.css 文件的內(nèi)容。
6、使用Flask-Moment本地化日期和時(shí)間
有一個(gè)使用 JavaScript 開發(fā)的優(yōu)秀客戶端開源代碼庫(kù),名為 moment.js(http://momentjs. com/),它可以在瀏覽器中渲染日期和時(shí)間。Flask-Moment 是一個(gè) Flask 程序擴(kuò)展,能把 moment.js 集成到 Jinja2 模板中。Flask-Moment 可以使用 pip 安裝:
pip3 install flask-moment
這個(gè)擴(kuò)展的初始化方法如示例 3-11 所示。 示例 3-11 hello.py:初始化 Flask-Moment
from flask.ext.moment import Moment moment = Moment(app)
7、使用Flask-Bootstrap集成Twitter Bootstrap
安裝
pip3 install flask-bootstrap
初始化 Flask-Bootstrap
from flask_bootstrap import Bootstrap
bootstrap = Bootstrap(app)
新建一個(gè)home.html模板,繼承bootstrap的base.html模板
{% extends "bootstrap/base.html" %}
{% block title %}Flasky{% endblock %}
{% block navbar %}
<div class="navbar navbar-inverse" role="navigation">
<div class="container">
<div class="navbar-header">
<button type="button" class="navbar-toggle"
data-toggle="collapse" data-target=".navbar-collapse">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">Flasky</a>
</div>
<div class="navbar-collapse collapse">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
</ul>
</div>
</div>
</div>
{% endblock %}
{% block content %} <div class="container">
<div class="page-header">
<h1>Hello, {{ name }}!</h1>
</div>
</div>
{% endblock %}
Flask-Bootstrap 的 base.html 模板還定義了很多其他塊,都可在衍生模板中使用。表 3-2 列


上圖中很多塊都是 Flask-Bootstrap 自用的,如果直接重定義可能會(huì)導(dǎo)致一些問(wèn)題。例 如,Bootstrap 所需的文件在 styles 和 scripts 塊中聲明。如果程序需要向已經(jīng)有內(nèi)容的塊 中添加新內(nèi)容,必須使用 Jinja2 提供的 super() 函數(shù)。例如,如果要在衍生模板中添加新 的 JavaScript 文件,需要這么定義 scripts 塊:
{% block scripts %}
{{ super() }}
<script type="text/javascript" src="my-script.js"></script>
{% endblock %}
7、web表單
盡管 Flask 的請(qǐng)求對(duì)象提供的信息足夠用于處理 Web 表單,但有些任務(wù)很單調(diào),而且要重 復(fù)操作。比如,生成表單的 HTML 代碼和驗(yàn)證提交的表單數(shù)據(jù)。
Flask-WTF(http://pythonhosted.org/Flask-WTF/)擴(kuò)展可以把處理 Web 表單的過(guò)程變成一 種愉悅的體驗(yàn)。這個(gè)擴(kuò)展對(duì)獨(dú)立的 WTForms(http://wtforms.simplecodes.com)包進(jìn)行了包 裝,方便集成到 Flask 程序中。
Flask-WTF 及其依賴可使用 pip 安裝:
pip3 install flask-wtf
默認(rèn)情況下,F(xiàn)lask-WTF能保護(hù)所有表單免受跨站請(qǐng)求偽造(Cross-Site Request Forgery,
CSRF)的攻擊。惡意網(wǎng)站把請(qǐng)求發(fā)送到被攻擊者已登錄的其他網(wǎng)站時(shí)就會(huì)引發(fā) CSRF 攻擊。 為了實(shí)現(xiàn) CSRF 保護(hù),F(xiàn)lask-WTF 需要程序設(shè)置一個(gè)密鑰。Flask-WTF 使用這個(gè)密鑰生成
加密令牌,再用令牌驗(yàn)證請(qǐng)求中表單數(shù)據(jù)的真?zhèn)?。設(shè)置密鑰的方法設(shè)置 Flask-WTF
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
33
app.config 字典可用來(lái)存儲(chǔ)框架、擴(kuò)展和程序本身的配置變量。使用標(biāo)準(zhǔn)的字典句法就能 把配置值添加到 app.config 對(duì)象中。這個(gè)對(duì)象還提供了一些方法,可以從文件或環(huán)境中導(dǎo) 入配置值。
SECRET_KEY 配置變量是通用密鑰,可在 Flask 和多個(gè)第三方擴(kuò)展中使用。如其名所示,加 密的強(qiáng)度取決于變量值的機(jī)密程度。不同的程序要使用不同的密鑰,而且要保證其他人不 知道你所用的字符串。
表單類:
使用 Flask-WTF 時(shí),每個(gè) Web 表單都由一個(gè)繼承自 Form 的類表示。這個(gè)類定義表單中的 一組字段,每個(gè)字段都用對(duì)象表示。字段對(duì)象可附屬一個(gè)或多個(gè)驗(yàn)證函數(shù)。驗(yàn)證函數(shù)用來(lái) 驗(yàn)證用戶提交的輸入值是否符合要求。
下面是一個(gè)簡(jiǎn)單的 Web 表單,包含一個(gè)文本字段和一個(gè)提交按鈕。
from flask_wtf import Form
from wtforms import StringField, SubmitField
from wtforms.validators import Required
class NameForm(Form):
name = StringField('What is your name?', validators=[Required()])
submit = SubmitField('Submit')
這個(gè)表單中的字段都定義為類變量,類變量的值是相應(yīng)字段類型的對(duì)象。在這個(gè)示例中, NameForm 表單中有一個(gè)名為 name 的文本字段和一個(gè)名為 submit 的提交按鈕。StringField 類表示屬性為 type="text" 的 <input> 元素。SubmitField 類表示屬性為 type="submit" 的 <input> 元素。字段構(gòu)造函數(shù)的第一個(gè)參數(shù)是把表單渲染成 HTML 時(shí)使用的標(biāo)號(hào)。
StringField 構(gòu)造函數(shù)中的可選參數(shù) validators 指定一個(gè)由驗(yàn)證函數(shù)組成的列表,在接受 用戶提交的數(shù)據(jù)之前驗(yàn)證數(shù)據(jù)。驗(yàn)證函數(shù) Required() 確保提交的字段不為空。
WTForms 支持的 HTML 標(biāo)準(zhǔn)字段如下圖

WTForms 內(nèi)建的驗(yàn)證函數(shù)

把表單類渲染為HTML
例通過(guò)參數(shù) form 傳入模板,在模板中可以生成一個(gè)簡(jiǎn)單的表單,如下所示:
<form method="POST">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
即便能指定 HTML 屬性,但按照這種方式渲染表單的工作量還是很大,所以在條件允許的 情況下最好能使用 Bootstrap 中的表單樣式。Flask-Bootstrap 提供了一個(gè)非常高端的輔助函 數(shù),可以使用 Bootstrap 中預(yù)先定義好的表單樣式渲染整個(gè) Flask-WTF 表單,而這些操作 只需一次調(diào)用即可完成。使用 Flask-Bootstrap,上述表單可使用下面的方式渲染:
{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form) }}
Flash消息
請(qǐng)求完成后,有時(shí)需要讓用戶知道狀態(tài)發(fā)生了變化。這里可以使用確認(rèn)消息、警告或者錯(cuò) 誤提醒。一個(gè)典型例子是,用戶提交了有一項(xiàng)錯(cuò)誤的登錄表單后,服務(wù)器發(fā)回的響應(yīng)重新 渲染了登錄表單,并在表單上面顯示一個(gè)消息,提示用戶用戶名或密碼錯(cuò)誤。
這種功能是 Flask 的核心特性。
@app.route('/',methods = ['GET','POST'])
def index():
name = None
form = NameForm()
if form.validate_on_submit():
old_name = session.get('name')
new_name = form.name.data
session['name'] = new_name
if old_name is not None and old_name != new_name:
flash('你似乎改變了你的名字')
return redirect(url_for('index'))
return render_template('testForm.html', form=form, name=session.get('name'))
在這個(gè)示例中,每次提交的名字都會(huì)和存儲(chǔ)在用戶會(huì)話中的名字進(jìn)行比較,而會(huì)話中存儲(chǔ) 的名字是前一次在這個(gè)表單中提交的數(shù)據(jù)。如果兩個(gè)名字不一樣,就會(huì)調(diào)用 flash() 函數(shù), 在發(fā)給客戶端的下一個(gè)響應(yīng)中顯示一個(gè)消息。
獲取get_flashed_messages()來(lái)獲取消息
在html中顯示消息
{% for message in get_flashed_messages() %}
<div class="alert alert-waring">
<button type="button" class="close" data-dismiss="alert">×</button>
{{message}}
</div>
{% endfor %}