1 程序的基本結構
1.1初始化
所有Flask 程序都必須創建一個程序實例。Web 服務器使用一種名爲Web 服務器網關接口(Web Server Gateway Interface,WSGI)的協議,把接收自客戶端的所有請求都轉交給這個對象處理。程序實例是Flask 類的對象。
from flask import Flask
app = Flask(__name__)
傳入Flask構造方法的第一個參數是模塊或者包的名稱,我們應該使用特殊變量 __name __。
Python會根據所處的模塊來賦予 __ name __變量相應的值,對於我們程序來說,這個值爲app
1.2註冊路由
客戶端(例如Web 瀏覽器)把請求發送給Web 服務器,Web 服務器再把請求發送給Flask
程序實例。程序實例需要知道對每個URL 請求運行哪些代碼,所以保存了一個URL 到Python 函數的映射關係。處理URL 和函數之間關係的程序稱爲路由。
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
在這個程序中,app.route()裝飾器把根地址/和index()函數綁定起來,當用戶訪問這個URL時就會觸發index()函數。視圖函數index()返回 " Hello World! "
route()裝飾器的第一個參數是URL規則,用字符串表示,必須以/開始。這裏的URL是相對的URL(又稱內部URL),即不包含域名的URL,以域名www.google.com爲例,“/”對應的是根地址,即www.google.com,如果把URL規則改爲“/maps”,則實際的絕對地址爲www.google.com/maps
動態路由
如果你仔細觀察日常所用服務的某些URL 格式,會發現很多地址中都包含可變部分。例如, 你的Facebook 資料頁面的地址是http://www.facebook.com/<your-name>, 用戶名(your-name)是地址的一部分。Flask 支持這種形式的URL,只需在route 修飾器中使用特殊的句法即可。下例定義的路由中就有一部分是動態名字:
@app.route('/user/<name>')
def user(name):
return '<h1>Hello, %s!</h1>' % name
1.3啓動服務器
程序實例用run 方法啓動Flask 集成的開發Web 服務器:
if __name__ == '__main__':
app.run(debug=True)
name__==' main__'是Python 的慣常用法,在這裏確保直接執行這個腳本時才啓動開發Web 服務器。如果這個腳本由其他腳本引入,程序假定父級腳本會啓動不同的服務器,因此不會執行app.run()。
1.4完整的程序
示例1.4 hello.py:一個完整的Flask 程序
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return '<h1>Hello World!</h1>'
@app.route('/user/<name>')
def user(name):
return '<h1>Hello, %s!</h1>' % name
if __name__ == '__main__':
app.run(debug=True)
1.4 異常捕獲
使用裝飾器的形式去捕獲指定的錯誤碼和異常
@app.errorhandler(404)
def page_not_found(error):
return '404'
1.5請求鉤子
爲了讓每個視圖函數避免編寫重複功能的代碼,Flask提供了通用設施的功能,即請求鉤子。
請求鉤子是通過裝飾器的形式實現,Flask支持如下四種請求鉤子:
- before_first_request:註冊一個函數,在處理第一個請求之前運行。
- before_request:註冊一個函數,在每次請求之前運行。
- after_request:註冊一個函數,如果沒有未處理的異常拋出,在每次請求之後運行。
- teardown_request:註冊一個函數,即使有未處理的異常拋出,也在每次請求之後運行。
--
2 模板
模板是一個包含響應文本的文件,其中包含用佔位變量表示的動態部分,其具體值只在請求的上下文中才能知道。使用真實值替換變量,再返回最終得到的響應字符串,這一過程稱爲渲染。爲了渲染模板,Flask 使用了一個名爲Jinja2 的強大模板引擎。
2.1 Jinja2模板引擎
- Jinjia2是由python實現的
- 是Flask內置的模板語言
- 參照Django設計思想設計的,跟Django差不多
2.1.1渲染模板
默認情況下,Flask 在程序文件夾中的templates 子文件夾中尋找模板。Flask提供的
示例 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)
示例 templates/index.html:Jinja2 模板
<h1>Hello World!</h1>
示例
<h1>Hello, {{ name }}!</h1>
render_template 函數封裝了該模板引擎
render_template 函數的第一個參數是模板的文件名,後面的參數都是鍵值對,表示模板中變量對應的真實值
2.1.2 變量
在模板中使用的{{ name }} 結構表示一個變量,它是一種特殊的佔位符,告訴模
板引擎這個位置的值從渲染模板時使用的數據中獲取。
可以使用過濾器修改變量,過濾器名添加在變量名之後,中間使用豎線分隔。例如,下述模板以首字母大寫形式顯示變量name 的值:
Hello, {{ name|capitalize }}
Jinja2 提供的部分常用過濾器
表 Jinja2變量過濾器
過濾器名 | 說明 |
---|---|
safe | 渲染值時不轉義 |
capitalize | 把值的首字母轉換成大寫,其他字母轉換成小寫 |
lower | 把值轉換成小寫形式 |
upper | 把值轉換成大寫形式 |
title | 把值中每個單詞的首字母都轉換成大寫 |
trim | 把值的首尾空格去掉 |
striptags | 渲染之前把值中所有的HTML 標籤都刪掉 |
2.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>
模板代碼複用
在模板中,可能會遇到以下問題
- 多個模板具有完全相同的頂部和底部內容
- 多個模板中具有相同的模板代碼內容,但是內容中部分值不一樣
- 多個模板中具有完全相同的 html 代碼塊內容
像遇到這種情況,可以使用 JinJa2 模板中的 宏、繼承、包含來進行實現
模板繼承
標籤定義的內容
{% block %} {% endblock %}
模板繼承是爲了重用模板中的公共內容。一般Web開發中,繼承主要使用在網站的頂部菜單、底部。這些內容可以定義在父模板中,子模板直接繼承,而不需要重複書寫。
首先創建一個名爲base.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 中。下面這個示例是基模板的衍生模板:
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ super() }}
<style>
</style>
{% endblock %}
{% block body %}
<h1>Hello, World!</h1>
{% endblock %}
extends 指令聲明這個模板衍生自base.html。在extends 指令之後,基模板中的3 個塊被
重新定義,模板引擎會將其插入適當的位置。注意新定義的head 塊,在基模板中其內容不
是空的,所以使用super() 獲取原來的內容。
2.2 Flask-Bootstrap
Bootstrap(http://getbootstrap.com/)是Twitter 開發的一個開源框架,它提供的用戶界面組
件可用於創建整潔且具有吸引力的網頁,而且這些網頁還能兼容所有現代Web 瀏覽器。
Bootstrap 是客戶端框架,因此不會直接涉及服務器。服務器需要做的只是提供引用了
Bootstrap 層疊樣式表(CSS) 和JavaScript 文件的HTML 響應, 並在HTML、CSS 和
JavaScript 代碼中實例化所需組件。這些操作最理想的執行場所就是模板。
- 初始化Flask-Bootstrap
from flask.ext.bootstrap import Bootstrap
# ...
bootstrap = Bootstrap(app)
初始化 Flask-Bootstrap 之後,就可以在程序中使用一個包含所有 Bootstrap 文件的基模板。這個模板利用Jinja2的模板繼承機制,讓程序擴展一個具有基本頁面結構的基模板,其中就有用來引入 Bootstrap 的元素。官方教程
{% extends "bootstrap/base.html" %}<!--extends 指 令 從 Flask-Bootstrap中導入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 %}
2.3 自定義錯誤頁面
像常規路由一樣,Flask 允許程序使用基於模板的自定義錯誤頁面。最常見的錯誤代碼有
兩個:404,客戶端請求未知頁面或路由時顯示;500,有未處理的異常時顯示。
例 hello.py:自定義錯誤頁面
@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
2.4 鏈接
在模板中直接編寫簡單路由的 URL 鏈接不難,但對於包含可變部分的動態路由,在模板中構建正確的 URL 就很困難。而且,直接編寫 URL 會對代碼中定義的路由產生不必要的依賴關係。如果重新定義路由,模板中的鏈接可能會失效。Flask 提供了 url_for() 輔助函數,它可以使用程序 URL 映射中保存的信息生成 URL。
2.5 靜態文件
用靜態文件,例如 HTML代碼中引用的圖片、JavaScript 源碼文件和 CSS。默認設置下,Flask 在程序根目錄中名爲 static 的子目錄中尋找靜態文件。如果需要,可在static 文件夾中使用子文件夾存放文件。
如索引圖標的位置{{ url_for('static', filename = 'favicon.ico') }}