寫代碼最關鍵的是要易於維護且結構清晰整潔。目前爲止,你看到的例子都過於簡單從而沒有作這方面的要求。Flask視圖函數但願將兩個應該徹底獨立的任務一併處理,兩個任務有兩種代碼,一併處理勢必會引起問題。html
明擺着的任務就是生成響應,就像你在第二章看到的示例那樣。對於最簡單的請求確實已經足夠,可是通常而言一個請求會觸發應用程序的狀態發生改變,而視圖函數就是產生這個改變的地方。python
例如,用戶在網站上註冊一個新的帳戶。用戶輸入郵箱地址和密碼到表單並點擊提交按鈕。一個包含數據的請求從用戶到服務器,Flask將其派發給視圖函數去處理這個註冊請求。這個視圖函數須要告訴數據庫添加新的用戶並生成一個響應發回給瀏覽器。這兩類任務一般被稱做業務邏輯和 顯示邏輯。git
混合業務邏輯和顯示邏輯會致使代碼很是的難以理解和維護。想象一下,將從數據庫獲取的數據和必要的HTML字符串文字進行鏈接,生成一堆的HTML代碼是多麼蛋疼的一件事情。而若是將顯示邏輯移到模板中去,則勢必極大提升應用程序的可維護性。github
模板就是包含響應文本的文件,帶有佔位符變量的動態部分只在請求上下文中被知道。用真實的值替換變量並返回最終響應字符串,這個過程稱爲渲染。對於渲染模板任務,Flask使用強大的模板引擎Jinja2。web
Jinja2模板最簡單的形式就是一個包含響應文本的文件。示例3-1展現了Jinja2模板匹配示例2-1中index()
視圖函數響應。數據庫
示例3-1. templates/index.html:Jinja2模板flask
html<h1>Hello World!</h1>
示例2-2中視圖函數user()
返回的響應有一個動態部分,該部分由一個變量表明。示例3-2展現模板實現該響應。api
示例3-2. templates/user.html:Jinja2模板瀏覽器
html<h1>Hello, {{ name }}!</h1>
Flask默認會在應用程序的templates子目錄中尋找模板。下個版本的hello.py
,你須要將以前定義的index.html
和user.html
模板存放到新的templates文件夾下。安全
應用程序中的視圖函數須要修改來渲染這些模板。示例3-3中會展現這些變化。
示例3-3. hello.py:渲染模板
pythonfrom flask import Flask, render_template # ... @app.route('/index') def index(): return render_template('index.html') @app.route('/user/<name>') def user(name): return render_template('user.html', name=name)
render_template函數由集成Jinja2模板引擎的Flask提供。這個函數將模板的文件名做爲它的第一個參數。任何附加的參數都是一個鍵/值對,分別對應被模板引用的變量和真實值。在這個示例中,第二個模板收到name變量。
相似於name=name
的關鍵字參數在前面的示例中是很是常見的,但若是你不使用就很容易混淆且很是難理解。左邊的「name」表明參數名稱,通常在模板中用於佔位符。右邊的「name」在當前範圍內是一個變量,爲同名的參數提供真實的值。
建議:若是你有克隆在GitHub上的應用程序,你如今能夠運行
git checkout 3a
來切換到這個版本的應用程序。
示例3-2的模板中的{{ name }}
結構引用一個變量,特殊的佔位符告訴模板引擎,那個地方的值應該從模板被渲染時提供的數據中獲取。
Jinja2識別任何類型的變量,包括複雜的列表、字典和對象。下面是一些模板中使用變量的示例:
html<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>
變量能夠經過filters來修改,以|
爲分隔符添加在變量名後。例如,下面的模板展現變量大寫:
htmlHello, {{ name|capitalize }}
表格3-1列出一些經常使用的Jinja2過濾器。
表格3-1. Jinja2變量過濾器
safe是比較有趣的一個過濾器。出於安全考慮,默認狀況下,Jinja2會轉義全部變量。例如,若是設置變量的值爲'<h1>Hello</h1>'
,Jinja2將渲染字符串爲'<h1>Hello</h1>'
,這將使得h1元素顯示出來且不能被瀏覽器解釋。不少時候有必要顯示存儲在變量中的HTML代碼,對於這些狀況就可使用safe過濾器。
警告:永遠不要在不信任的值中使用safe過濾器,例如用戶在web表單中輸入的文本。
完整的過濾器列表能夠從Jinja2官方文檔中獲取。
Jinja2提供一些控制結構用於改變模板流。這一節用簡單例子來介紹一些最有用的。
下面的示例展現條件語句是怎樣在模板中使用的:
{ % if user %} Hello, {{ user }}! { % else %} Hello, Stranger! { % endif %}
渲染一列元素在模板中是常常用到的。這個示例展現如何使用for
循環作到這些:
html<ul> {% for comment in comments %} <li>{{ comment }}</li> {% endfor %} </ul>
Jinja2一樣支持宏,這和Python代碼中的函數很像。例如:
html{% macro render_comment(comment) %} <li>{{ comment }}</li> {% endmacro %} <ul> {% for comment in comments %} {{ render_comment(comment) }} {% endfor %} </ul>
爲了提升宏的複用性,能夠將它們保存爲單獨的文件,而後在全部須要的地方import便可:
html{% import 'macros.html' as macros %} <ul> {% for comment in comments %} {{ macros.render_comment(comment) }} {% endfor %} </ul>
須要在一些地方重複使用的模板代碼能夠保存爲一個單獨的文件,全部模板均可以include它來避免重複:
{% include 'common.html' %}
另外一個強大的複用方式是經過模板繼承,這相似於Python代碼中類的繼承。首先建立一個名爲base.html
的基礎模板:
html<html> <head> {% block head %} <title>{% block title %}{% endblock %} - My Application</title> {% endblock %} </head> <body> {% block body %} {% endblock %} </body> </html>
這裏block
標籤訂義的派生模板是可變的。在這個示例中,有head
、title
、body
塊;注意title
包含在head
中。下面的示例是一個基礎模塊的派生模塊:
html{% extends "base.html" %} {% block title %}Index{% endblock %} {% block head %} {{ super() }} <style> </style> {% endblock %} {% block body %} <h1>Hello, World!</h1> {% endblock %}
extends指令定義這個模板由base.html
派生。緊隨這個指令以後的是三個在基礎模板中定義的塊,它們將在合適的位置插入。注意在基礎模板中新定義的head
塊是不爲空的,因此使用super()
保留原來的內容。
本節呈現的全部控制結構的實際運用將在後面展現,你會有機會看到它們是怎樣工做的。