默認情況下啄清,F(xiàn)lask 在程序文件夾中的 templates 子文件夾中尋找模板空免。在下一個 hello.py 版本中,要把前面定義的模板保存在 templates 文件夾中款筑,并分別命名為 index.html 和 user.html腋么。
index.thm的代碼如下,不傳參數(shù)
<html>
<head>
<title>歡迎來到王者榮耀</title>
</head>
<body>
<p>你是不是要成為一個高手</p>
</body>
</html>
user.html中傳遞一個參數(shù)
<html>
<head>
<title>歡迎來到王者榮耀</title>
</head>
<body>
<p>你是不是要成為一個高手咕娄,你的名字是{{name}}</p>
</body>
</html>
在hello.py中,
from flask import render_template
@app.route('/')
def index():
return render_template('index.html')
@app.route('/user/<name>') #尖括號中的動態(tài)名字
def user(name):
return render_template('user.html',name=name)
1珊擂、變量
在模板中使用的 {{ name }}
結(jié)構(gòu)表示一個變量圣勒,它是一種特殊的占位符,告訴模
板引擎這個位置的值從渲染模板時使用的數(shù)據(jù)中獲取摧扇。
Jinja2 能識別所有類型的變量圣贸,甚至是一些復雜的類型,例如列表扳剿、字典和對象旁趟。在模板 中使用變量的一些示例如下:
<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>
可以使用過濾器修改變量昼激,過濾器名添加在變量名之后庇绽,中間使用豎線分隔锡搜。例如,下述 模板以首字母大寫形式顯示變量 name 的值:
Hello, {{ name|capitalize }}
下面是常用的過濾器
更多的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>
為了重復使用宏辟狈,我們可以將其保存在單獨的文件中肠缔,然后在需要使用的模板中導入:
{% import 'macros.html' as macros %}
<ul>
{% for comment in comments %}
{{ macros.render_comment(comment) }}
{% endfor %}
</ul>
需要在多處重復使用的模板代碼片段可以寫入單獨的文件,再包含在所有模板中哼转,以避免 重復:
{% include 'common.html' %}
另一種重復使用代碼的強大方式是模板繼承明未,它類似于 Python 代碼中的類繼承。首先壹蔓,創(chuàng)
建一個名為 base.html 的基模板:
<html>
<head>
{% block head %}
<title>{% block title %}{% endblock %} - MyApplication</title>
{% endblock %}
</head>
<body>
{%block body %}
<p>我是來自基類</p>
{%endblock %}
</body>
</html>
新建一個son.html 繼承base.html
{% extends "base.html" %}
{% block title %} INDEX {% endblock %}
{% block head %}
{{super()}}
{% endblock %}
{% block body %}
{{super()}}
<h1>我也是醉了趟妥,這是啥基礎(chǔ)自基礎(chǔ)模板</h1>
{% endblock %}
extends 指令聲明這個模板衍生自 base.html。在 extends 指令之后佣蓉,基模板中的 3 個塊被 重新定義披摄,模板引擎會將其插入適當?shù)奈恢谩W⒁庑露x的 head 塊勇凭,在基模板中其內(nèi)容不 是空的疚膊,所以使用 super() 獲取原來的內(nèi)容。
3虾标、自定義錯誤頁面的方式
自定義錯誤頁面
@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寓盗、鏈接
任何具有多個路由的程序都需要可以連接不同頁面的鏈接,例如導航條璧函。
在模板中直接編寫簡單路由的 URL 鏈接不難贞让,但對于包含可變部分的動態(tài)路由,在模板 中構(gòu)建正確的 URL 就很困難柳譬。而且喳张,直接編寫 URL 會對代碼中定義的路由產(chǎn)生不必要的 依賴關(guān)系。如果重新定義路由美澳,模板中的鏈接可能會失效销部。
為了避免這些問題,F(xiàn)lask 提供了 url_for() 輔助函數(shù)制跟,它可以使用程序 URL 映射中保存 的信息生成 URL舅桩。
url_for() 函數(shù)最簡單的用法是以視圖函數(shù)名(或者 app.add_url_route() 定義路由時使用 的端點名)作為參數(shù),返回對應的 URL雨膨。例如擂涛,在當前版本的 hello.py程序中調(diào)用 url_ for('index')得到的結(jié)果是/。調(diào)用url_for('index', _external=True)返回的則是絕對地 址聊记,在這個示例中是 http://localhost:5000/撒妈。
生成連接程序內(nèi)不同路由的鏈接時恢暖,使用相對地址就足夠了。如果要生成在 瀏覽器之外使用的鏈接狰右,則必須使用絕對地址杰捂,例如在電子郵件中發(fā)送的 鏈接。
使用 url_for() 生成動態(tài)地址時棋蚌,將動態(tài)部分作為關(guān)鍵字參數(shù)傳入嫁佳。例如,url_for ('user', name='john', _external=True) 的返回結(jié)果是 http://localhost:5000/user/john谷暮。
傳入 url_for() 的關(guān)鍵字參數(shù)不僅限于動態(tài)路由中的參數(shù)蒿往。函數(shù)能將任何額外參數(shù)添加到 查詢字符串中。例如湿弦,url_for('index', page=2) 的返回結(jié)果是 /?page=2熄浓。
5、靜態(tài)文件
Web 程序不是僅由 Python 代碼和模板組成省撑。大多數(shù)程序還會使用靜態(tài)文件赌蔑,例如 HTML
代碼中引用的圖片、JavaScript 源碼文件和 CSS竟秫。
例如娃惯,調(diào)用 url_for('static', filename='css/styles.css', _external=True) 得 到 的 結(jié) 果 是 http:// localhost:5000/static/css/styles.css。
默認設(shè)置下肥败,F(xiàn)lask 在程序根目錄中名為 static 的子目錄中尋找靜態(tài)文件趾浅。如果需要,可在 static 文件夾中使用子文件夾存放文件馒稍。服務器收到前面那個 URL 后皿哨,會生成一個響應, 包含文件系統(tǒng)中 static/css/styles.css 文件的內(nèi)容纽谒。
6证膨、使用Flask-Moment本地化日期和時間
有一個使用 JavaScript 開發(fā)的優(yōu)秀客戶端開源代碼庫,名為 moment.js(http://momentjs. com/)鼓黔,它可以在瀏覽器中渲染日期和時間央勒。Flask-Moment 是一個 Flask 程序擴展,能把 moment.js 集成到 Jinja2 模板中澳化。Flask-Moment 可以使用 pip 安裝:
pip3 install flask-moment
這個擴展的初始化方法如示例 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)
新建一個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 自用的,如果直接重定義可能會導致一些問題瑞你。例 如酪惭,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 的請求對象提供的信息足夠用于處理 Web 表單纺铭,但有些任務很單調(diào)寇钉,而且要重 復操作。比如舶赔,生成表單的 HTML 代碼和驗證提交的表單數(shù)據(jù)扫倡。
Flask-WTF(http://pythonhosted.org/Flask-WTF/)擴展可以把處理 Web 表單的過程變成一 種愉悅的體驗。這個擴展對獨立的 WTForms(http://wtforms.simplecodes.com)包進行了包 裝竟纳,方便集成到 Flask 程序中撵溃。
Flask-WTF 及其依賴可使用 pip 安裝:
pip3 install flask-wtf
默認情況下,F(xiàn)lask-WTF能保護所有表單免受跨站請求偽造(Cross-Site Request Forgery锥累,
CSRF)的攻擊缘挑。惡意網(wǎng)站把請求發(fā)送到被攻擊者已登錄的其他網(wǎng)站時就會引發(fā) CSRF 攻擊。 為了實現(xiàn) CSRF 保護桶略,F(xiàn)lask-WTF 需要程序設(shè)置一個密鑰语淘。Flask-WTF 使用這個密鑰生成
加密令牌,再用令牌驗證請求中表單數(shù)據(jù)的真?zhèn)渭始摺TO(shè)置密鑰的方法設(shè)置 Flask-WTF
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
33
app.config 字典可用來存儲框架惶翻、擴展和程序本身的配置變量。使用標準的字典句法就能 把配置值添加到 app.config 對象中鹅心。這個對象還提供了一些方法吕粗,可以從文件或環(huán)境中導 入配置值。
SECRET_KEY 配置變量是通用密鑰旭愧,可在 Flask 和多個第三方擴展中使用溯泣。如其名所示,加 密的強度取決于變量值的機密程度榕茧。不同的程序要使用不同的密鑰垃沦,而且要保證其他人不 知道你所用的字符串。
表單類:
使用 Flask-WTF 時用押,每個 Web 表單都由一個繼承自 Form 的類表示肢簿。這個類定義表單中的 一組字段,每個字段都用對象表示。字段對象可附屬一個或多個驗證函數(shù)池充。驗證函數(shù)用來 驗證用戶提交的輸入值是否符合要求桩引。
下面是一個簡單的 Web 表單,包含一個文本字段和一個提交按鈕收夸。
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')
這個表單中的字段都定義為類變量坑匠,類變量的值是相應字段類型的對象。在這個示例中卧惜, NameForm 表單中有一個名為 name 的文本字段和一個名為 submit 的提交按鈕厘灼。StringField 類表示屬性為 type="text" 的 <input> 元素。SubmitField 類表示屬性為 type="submit" 的 <input> 元素咽瓷。字段構(gòu)造函數(shù)的第一個參數(shù)是把表單渲染成 HTML 時使用的標號设凹。
StringField 構(gòu)造函數(shù)中的可選參數(shù) validators 指定一個由驗證函數(shù)組成的列表,在接受 用戶提交的數(shù)據(jù)之前驗證數(shù)據(jù)茅姜。驗證函數(shù) Required() 確保提交的字段不為空闪朱。
WTForms 支持的 HTML 標準字段如下圖
WTForms 內(nèi)建的驗證函數(shù)
把表單類渲染為HTML
例通過參數(shù) form 傳入模板,在模板中可以生成一個簡單的表單钻洒,如下所示:
<form method="POST">
{{ form.hidden_tag() }}
{{ form.name.label }} {{ form.name() }}
{{ form.submit() }}
</form>
即便能指定 HTML 屬性奋姿,但按照這種方式渲染表單的工作量還是很大,所以在條件允許的 情況下最好能使用 Bootstrap 中的表單樣式素标。Flask-Bootstrap 提供了一個非常高端的輔助函 數(shù)称诗,可以使用 Bootstrap 中預先定義好的表單樣式渲染整個 Flask-WTF 表單,而這些操作 只需一次調(diào)用即可完成糯钙。使用 Flask-Bootstrap粪狼,上述表單可使用下面的方式渲染:
{% import "bootstrap/wtf.html" as wtf %}
{{ wtf.quick_form(form) }}
Flash消息
請求完成后,有時需要讓用戶知道狀態(tài)發(fā)生了變化任岸。這里可以使用確認消息再榄、警告或者錯 誤提醒。一個典型例子是享潜,用戶提交了有一項錯誤的登錄表單后困鸥,服務器發(fā)回的響應重新 渲染了登錄表單,并在表單上面顯示一個消息剑按,提示用戶用戶名或密碼錯誤疾就。
這種功能是 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'))
在這個示例中艺蝴,每次提交的名字都會和存儲在用戶會話中的名字進行比較猬腰,而會話中存儲 的名字是前一次在這個表單中提交的數(shù)據(jù)。如果兩個名字不一樣猜敢,就會調(diào)用 flash() 函數(shù)姑荷, 在發(fā)給客戶端的下一個響應中顯示一個消息盒延。
獲取get_flashed_messages()
來獲取消息
在html中顯示消息
{% for message in get_flashed_messages() %}
<div class="alert alert-waring">
<button type="button" class="close" data-dismiss="alert">×</button>
{{message}}
</div>
{% endfor %}