chapter3 - 使用模板
3.1 Jinja2模板引擎
個人覺得模板主要用於前端的顯示部分。模板中用到python語句的地方,需要用{% %}包圍起來,並且有開始和結束兩行,而變數名則需要用兩個大括弧包圍:{{ }}。如下面為一個條件控制語句:
{% if user %} Hello, {{ user }}!{% else %} Hello, Stranger!{% endif %}
模板中需要注釋掉部分語句時,也需要用一對大括弧內加一對#號包圍,如:
{# for message in messages #}
上面代碼中的for循環將被注釋掉而不會被執行
3.1.1 渲染模板
Jinja2是Flask默認使用的模板引擎。默認情況下,Flask程序在程序文件夾中的templates子文件夾中尋找模板。簡單的模板使用示例:
from flask import Flaskfrom flask import render_templateapp = Flask(__name__)@app.route(/)def index(): return render_template(index.html)@app.route(/user/<name>)def user(name): return render_template(user.html, name=name)if __name__ == __main__: manager.run()
如上,render_template函數的第一個參數是模板的文件名,後面的參數都是鍵值對,表示模板文件中接收的變數。如上的user()函數中,將會把name傳給user.html文件。最簡單的user.html文件(位置為templates/user.html)可以是:
<h1>Hello, {{ name }}!</h1>
由此,通過user()函數傳入的name參數將會在模板中的相應位置顯示
3.1.2 變數
Jinja2可以識別所有類型的變數,包括列表、字典、對象。也可以使用過濾器修改變數,變數名和過濾器之間使用豎線分隔。如下述模板以首字母大寫形式顯示變數name的值:
Hello, {{ name|capitalize }}
以下是Jinja2提供的部分常用的過濾器:
過濾器名說明safe渲染值時不轉義capitalize把值的首字母轉換成大寫,其它字母轉換成小寫lower把值轉換成小寫形式upper把值轉換成大寫形式title把值中每個單詞的首字母都轉換成大寫trim把值的首尾空格去掉striptags渲染之前把值中所有的HTML標籤都刪掉值得注意的是safe過濾器。默認情況下,Jinja2會轉義所有變數。如果把 <h1>Hello</h1>轉換成<h1>Hello</h1>,如果需要顯示變數中存儲的HTML代碼,就可以使用safe過濾器,但千萬不要在不可信的值上使用safe過濾器。完整的過濾器列表在:Template Designer Documentation
3.1.3 控制結構
Jinja2的控制結構與一般編程語言的控制語句有點類似。
- 條件控制語句
{% if user %} Hello, {{ user }}!{% else %} Hello, Stranger!{% endif %}
- 渲染一組元素
<ul> {% for comment in comments %} <li>{{ comment }}</li> {% endfor %}</ul>
- 使用宏
# 定義宏{% 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中的類繼承。先寫一個名為base.html的基模板:
<html><head> {% block head %} <title>{% block title %}{% endblock %} - My Application</title> {% endblock %}<body> {% block body %} {% endblock %}</body></head></html>
如上,在block標籤中的元素是可以在衍生模板中修改的,如:
{% extends "base.html" %}{% block title %}Index{% endblock %}{% block head %} {{ super() }} <style> </style>{% endblock %}{% block body %}<h1>Hello, World!</h1>{% endblock %}
extends指令聲明這個模板衍生自base.html。其後基模板中的3個塊title、head、body被重新定義。注意在新定義的head塊中,因為在基模板中其內容不是空的,所以要使用super()來獲取原來的內容
模板中的所有block都必須要有結束的block語句3.2 使用Flask-Bootstrap集成Twitter Bootstrap
Bootstrap是一個前端的js框架,可以用創建優雅的UI。可以使用pip安裝:(venv) $ pip install flask-bootstrap
初始化Flask-Bootstrap
在代碼中加入:
# 原書中的代碼是 from flask.ext.bootstrap import Bootstrap# 但是我在python3.5中運行時,被提示說這種用法已經過時了# 而應該使用下面這種導入方法from flask_bootstrap import Bootstrap#...# 下面這行代碼應在app = Flask(__name__)之後bootstrap = Bootstrap(app)#...
- 在模板中使用Flask-Bootstrap
修改templates/base.html文件:
{% extends "bootstrap/base.html" %}{% block title %}Flasky{% endblock %}{% block navbar %}<div class="navbar navbar-invrese" 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 %}
以上模板中,定義了3個塊,分別是title、navbar、content,這些塊都是基模板提供的,可以在這裡重新定義。其中navbar和content兩個塊分別表示頁面中的導航條的主體內容。更多的Bootstrap信息可參考Bootstrap官網。
Flask-Bootstrap的base.html模板還定義了很多其它塊,都可以在衍生模板中使用:塊名說明doc整個HTML文檔html_attribs<html>標籤中的屬性html<html>標籤中的內容head<head>標籤中的內容title<title>標籤中的內容metas一組<meta>標籤styles層疊樣式表CSS定義body_attribs<body>標籤的屬性body<body>標籤中的內容navbar用戶定義的導航條content用戶定義的頁面內容scripts文檔底部的JavaScript聲明
但應注意的是,上表中的很多塊都是Flask-Bootstrap自用的,直接重定義可能會出現問題,例如,Bootstrap所需的文件在sytles和scripts塊中聲明,如果需要添加新內容,則必須使用Jinja2的super()函數,例如:
{% block scripts %}{{ super() }}<script type="text/javascript" src="my-script.js">/script>{% endblock %}}
這樣就不會覆蓋掉基模板中的原有內容
3.3 自定義錯誤頁面
在沒有定義錯誤頁面之前,如果訪問一個不存在的URL,瀏覽器會顯示一個它內置的404頁面。Flask提供了errorhandler()修飾器,允許程序自定義錯誤頁面。如下兩段代碼可以指定特定的404和500錯誤頁面:
@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
為了方便使用,作者在此處把前面定義好的user.html模板改寫作為項目裡面的基模板base.html,然後再新建user.html模板繼承自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"> {% block page_content %}{% endblock %}</div>{% endblock %}
基模板主要定義了默認標題和導航欄內容,內容部分content block只放了一個div,其中包含了一個名為page_content的空白塊,塊中的內容由衍生模板定義,作為頁面主體內容顯示。
由此重新組織的templates/user.html頁面為:{% extends "base.html" %}{% block title %}Flasky{% endblock %}{% block page_content %}<div class="page-header"> <h1>Hello, {{ name }}!</h1></div>{% endblock %}
404錯誤頁面templates/404.html
{% extends "base.html" %}{% block title %}Flasky - Page Not Found{% endblock %}{% block page_content %}<div class="page-header"> <h1>Not Found</h1></div>{% endblock %}
詳細內容可參考Jinja2中文文檔
推薦閱讀:
※Python 與 機器學習 · 簡介
※一步一步教你用Python畫出專業的K線圖
※pyecharts 更新至 v0.2.6 版本啦