已經(jīng)解釋的非常詳細(xì)了,我這里只把我關(guān)注的點(diǎn)以我的理解記錄一下。
一:程序的基本結(jié)構(gòu)
1. 初始化
2. 路由和視圖函數(shù)
- flask使用route裝飾器把一個函數(shù)綁定到對應(yīng)的 URL 上,把修飾的函數(shù)注冊為路由
- URL 使用尖括號 <variable_name> , flask將這個部分作為命名參數(shù)傳遞到你的函數(shù)??梢越o該參數(shù)定義轉(zhuǎn)換器實(shí)現(xiàn)類型轉(zhuǎn)換。
轉(zhuǎn)換器又:int,float,path
@app.route('/user/<username>')
def show_user_profile(username):
return 'User %s' % username
@app.route('/post/<int:post_id>')
def show_post(post_id):
return 'Post %d' % post_id
3. 程序和請求的上下文
Flask使用在 Flask 中由全局的
request對象來提供這些信息客戶端交互信息。Flask 從客戶端收到請求時,要先讓視圖函數(shù)能訪問一些對象,這樣才能處理請求。例如request對象封裝了客戶端發(fā)送的 HTTP 請求。
Flask 使用上下文臨時把某些對象變?yōu)槿挚稍L問。
-
在 Flask 中有兩種上下文:程序上下文和請求上下文
flask上下文 Flask 在分發(fā)請求之前激活(或推送)程序和請求上下文,請求處理完成后再將其刪除
在程序?qū)嵗险{(diào)用 app.app_context() 可獲得一個程序上 下文。
Flask 中的某些對象是全局對象,但卻不是通常的那種。這些對象實(shí)際上是特定環(huán)境的局部對象的代理。雖然很拗口,但實(shí)際上很容易理解。
想象一下處理線程的環(huán)境。一個請求傳入,Web 服務(wù)器決定生成一個新線程( 或者別的什么東西,只要這個底層的對象可以勝任并發(fā)系統(tǒng),而不僅僅是線程)。 當(dāng) Flask 開始它內(nèi)部的請求處理時,它認(rèn)定當(dāng)前線程是活動的環(huán)境,并綁定當(dāng)前的應(yīng)用和 WSGI 環(huán)境到那個環(huán)境上(線程)。它的實(shí)現(xiàn)很巧妙,能保證一個應(yīng)用調(diào)用另一個應(yīng)用時不會出現(xiàn)問題。
所以,這對你來說意味著什么?除非你要做類似單元測試的東西,否則你基本上可以完全無視它。你會發(fā)現(xiàn)依賴于一段請求對象的代碼,因沒有請求對象無法正常運(yùn)行。解決方案是,自行創(chuàng)建一個請求對象并且把它綁定到環(huán)境中。單元測試的最簡單的解決方案是:用 <tt class="xref py py-meth docutils literal" style="font-family: Consolas, Menlo, "Deja Vu Sans Mono", "Bitstream Vera Sans Mono", monospace; font-size: 0.9em; background-color: rgb(251, 251, 251); color: rgb(34, 34, 34); font-weight: bold; border-bottom-width: 1px; border-bottom-style: solid; border-bottom-color: white;">test_request_context()</tt> 環(huán)境管理器。結(jié)合 <cite>with</cite> 聲明,綁定一個測試請求,這樣你才能與之交互
4. 請求調(diào)度
- 程序收到客戶端發(fā)來的請求時,要找到處理該請求的視圖函數(shù)。為了完成這個任務(wù),F(xiàn)lask 會在程序的 URL 映射中查找請求的 URL。URL 映射是 URL 和視圖函數(shù)之間的對應(yīng)關(guān)系。 Flask 使用 app.route 修飾器或者非修飾器形式的 app.add_url_rule() 生成映射。
- URL 映射中的 HEAD、Options、GET 是請求方法,由路由進(jìn)行處理。Flask 為每個路由都指 定了請求方法,這樣不同的請求方法發(fā)送到相同的 URL 上時,會使用不同的視圖函數(shù)進(jìn) 行處理。
5. 請求鉤子
- 有時需要在處理請求之前或之后執(zhí)行代碼。例如,認(rèn)證發(fā)起請求的用戶。為了避免在每個視圖函數(shù)中都使用重復(fù)的代碼, Flask 提供了注冊通用函數(shù)的功能,注冊的函數(shù)可在請求被分發(fā)到視圖函數(shù)之前或之后調(diào)用。這種程序叫做鉤子函數(shù)。
- 請求鉤子使用修飾器實(shí)現(xiàn)
- flask支持以下幾種鉤子
- before_first_request:注冊一個函數(shù),在處理第一個請求之前運(yùn)行
- before_request:注冊一個函數(shù),在每次請求之前運(yùn)行
- after_request:注冊一個函數(shù),如果沒有未處理的異常拋出,在每次請求之后運(yùn)行
- teardown_request:注冊一個函數(shù),即使有未處理的異常拋出,也在每次請求之后運(yùn)行。
- 在請求鉤子函數(shù)和視圖函數(shù)之間共享數(shù)據(jù)一般使用上下文全局變量 g,例如,before_ request 處理程序可以從數(shù)據(jù)庫中加載已登錄用戶,并將其保存到 g.user 中。隨后調(diào)用視 圖函數(shù)時,視圖函數(shù)再使用 g.user 獲取用戶。
6. 響應(yīng)
- Flask 視圖函數(shù)一般返回一個 Response 對象。make_response() 函數(shù)可接受 1 個、2 個或 3 個參數(shù)(和視圖函數(shù)的返回值一樣),并返回一個 Response 對象。
from flask import make_response
@app.route('/')
def index():
response = make_response('<h1>This document carries a cookie!</h1>')
response.set_cookie('answer', '42')
return response
- 重定向是一種特殊的響應(yīng),響應(yīng)內(nèi)容是 URL,而不是包含 HTML 代碼的字符串。瀏覽器收到 這種響應(yīng)時,會向重定向的 URL 發(fā)起 GET 請求,顯示頁面的內(nèi)容。
- 重定向響應(yīng)可以使用 3 個值形式的返回值生成,也可在 Response 對象中設(shè)定。不過,由于使用頻繁,F(xiàn)lask 提 供了 redirect() 輔助函數(shù),
from flask import redirect
@app.route('/')
def index():
return redirect('http://www.example.com')
- 可以使用redirect() 輔助函數(shù), 生成 HTTP 重定向響應(yīng)。redirect() 函數(shù)的參數(shù)是重定向的 URL,推薦使用 url_for() 生成 URL,因為這 個函數(shù)使用 URL 映射生成 URL,從而保證 URL 和定義的路由兼容,而且修改路由名字后 依然可用。
- url_for() 函數(shù)的第一個且唯一必須指定的參數(shù)是端點(diǎn)名,即路由的內(nèi)部名字。默認(rèn)情 況下,路由的端點(diǎn)是相應(yīng)視圖函數(shù)的名字。
- 重定向時請求數(shù)據(jù)會request上下文的內(nèi)容會丟失,因為重定向相當(dāng)于另一個請求,這時程序可以把數(shù)據(jù)存儲在session中,在請求之間“記住”數(shù)據(jù)。session是一種私有存 儲,存在于每個連接到服務(wù)器的客戶端中。默認(rèn)情況下,session保存在客戶端 cookie 中
from flask import Flask, render_template, session, redirect, url_for
@app.route('/', methods=['GET', 'POST'])
def index():
form = NameForm()
if form.validate_on_submit():
session['name'] = form.name.data
return redirect(url_for('index'))
return render_template('index.html', form=form, name=session.get('name'))
flask擴(kuò)展
社區(qū)成員開發(fā)了大量不同用途的flask擴(kuò)展,下面介紹如何把擴(kuò)展整合到程序中
- 使用pip安裝flask擴(kuò)展
- 在程序中使用
from flask.ext.script import Managerflask.ext模塊導(dǎo)入該擴(kuò)展
- 專為 Flask 開發(fā)的擴(kuò)展都暴漏在 flask.ext 命名空間下
二:模版
- 默認(rèn)情況下,F(xiàn)lask 在程序文件夾中的 templates 子文件夾中尋找模板
- flask支持很多模版引擎,
render_template函數(shù)把Jinja2模板引擎集成到了程序中 -
render_template函 數(shù)的第一個參數(shù)是模板的文件名。隨后的參數(shù)都是鍵值對,表示模板中變量對應(yīng)的真實(shí)值
from flask import Flask, render_template # ...
@app.route('/user/<name>')
def user(name):
return render_template('user.html', name=name)
1. 變量
- 在模板中使用的
{{ name }}結(jié)構(gòu)表示一個變量,它是一種特殊的占位符,告訴模板引擎這個位置的值從渲染模板時使用的數(shù)據(jù)中獲取 - 可以使用過濾器修改變量,過濾器名添加在變量名之后,中間使用豎線分隔
-
Jinja2 提供的部分常用過濾器:
Jinja2 提供的部分常用過濾器
2. jinja2控制結(jié)構(gòu)
- Jinja2 提供了多種控制結(jié)構(gòu),可用來改變模板的渲染流程
條件控制語句
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, Stranger!
{% endif %}
for循環(huán)
<ul>
{% for comment in comments %}
<li>{{ comment }}</li> {% endfor %}
</ul>
引入別的文件
{% include 'common.html' %}
- 需要在多處重復(fù)使用的模板代碼片段可以寫入單獨(dú)的文件,再包含在所有模板中,以避免 重復(fù):
- 模版繼承
base.html
<html>
<head>
{% block head %} //聲明一個代碼塊
<title>{% block title %}{% endblock %} - My Application</title> {% endblock %}
</head>
<body>
{% block body %}
{% endblock %} </body>
</html>
{% extends "base.html" %} 聲明這個模板衍生自 base.html。
{% block title %}Index{% endblock %} {% block head %} //覆蓋父類代碼塊
{{ super() }}
<style>
</style>
{% endblock %}
{% block body %} <h1>Hello, World!</h1> {% endblock %}
推薦使用flask-bootstrap框架
- Flask-Bootstrap 中的基模板提供了一個網(wǎng)頁框架,引入了 Bootstrap 中的所有 CSS 和JavaScript 文件。
3. 自定義錯誤顯示頁面
- Flask 允許程序使用基于模板的自定義錯誤頁面
4.鏈接
- Flask 提供了 url_for() 輔助函數(shù),它可以使用程序 URL 映射中保存 的信息生成 URL
- url_for() 函數(shù)最簡單的用法是以視圖函數(shù)名(或者 app.add_url_route() 定義路由時使用 的端點(diǎn)名)作為參數(shù),返回對應(yīng)的 URL。
- 使用 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)文件
- 默認(rèn)設(shè)置下,F(xiàn)lask 在程序根目錄中名為 static 的子目錄中尋找靜態(tài)文件
{% block head %}
{{ super() }}
<link rel="shortcut icon" href="{{ url_for('static', filename = 'favicon.ico') }}"
type="image/x-icon">
<link rel="icon" href="{{ url_for('static', filename = 'favicon.ico') }}"
type="image/x-icon"> {% endblock %}
四:web表單
- request.form 能獲取 POST 請求中提交的表單數(shù)據(jù)。
- Flask-WTF擴(kuò)展可以把處理 Web 表單的過程變成一 種愉悅的體驗。
1. 跨站請求偽造保護(hù)
- 默認(rèn)情況下,F(xiàn)lask-WTF 能保護(hù)所有表單免受跨站請求偽造(CSRF)的攻擊
- 為了實(shí)現(xiàn) CSRF 保護(hù),F(xiàn)lask-WTF 需要程序設(shè)置一個密鑰。Flask-WTF 使用這個密鑰生成加密令牌,再用令牌驗證請求中表單數(shù)據(jù)的真?zhèn)?/li>
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
- app.config 字典可用來存儲框架、擴(kuò)展和程序本身的配置變量。
4.2 表單類
- 類定義表單中的 一組字段,每個字段都用對象表示。字段對象可附屬一個或多個驗證函數(shù)。驗證函數(shù)用來 驗證用戶提交的輸入值是否符合要求。
from flask.ext.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òu)造函數(shù)的第一個參數(shù)是把表單渲染成 HTML 時使用的標(biāo)號
//StringField 構(gòu)造函數(shù)中的可選參數(shù) validators 指定一個由驗證函數(shù)組成的列表,在接受 用戶提交的數(shù)據(jù)之前驗證數(shù)據(jù)。驗證函數(shù) Required() 確保提交的字段不為空
3.把表單渲染成HTML
- 表單字段是可調(diào)用的,在模板中調(diào)用后會渲染成 HTML。
- Flask-Bootstrap 提供了一個非常高端的輔助函 數(shù),可以使用 Bootstrap 中預(yù)先定義好的表單樣式渲染整個 Flask-WTF 表單
{% extends "base.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}Flasky{% endblock %}
{% block page_content %} <div class="page-header">
<h1>Hello, {% if name %}{{ name }}{% else %}Stranger{% endif %}!</h1> </div>
{{ wtf.quick_form(form) }} {% endblock %}
- 在視圖函數(shù)中處理表單
@app.route('/', methods=['GET', 'POST'])
def index():
name = None
form = NameForm() 在視圖函數(shù)中創(chuàng)建一個 NameForm 類實(shí)例用于表示表單
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('index.html', form=form, name=name)
- Flash消息
- flash() 函數(shù)
七:大型程序的結(jié)構(gòu)
import os
basedir = os.path.abspath(os.path.dirname(__file__))
class Config: #基類 Config 中包含通用配置,子類分別定義專用的配置。
SECRET_KEY = os.environ.get('SECRET_KEY') or 'hard to guess string'
SQLALCHEMY_COMMIT_ON_TEARDOWN = True
FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>'
FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')
@staticmethod
def init_app(app):
pass
class DevelopmentConfig(Config):
DEBUG = True
MAIL_SERVER = 'smtp.googlemail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
SQLALCHEMY_DATABASE_URI = os.environ.get('DEV_DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')
class TestingConfig(Config):
TESTING = True
SQLALCHEMY_DATABASE_URI = os.environ.get('TEST_DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data-test.sqlite')
class ProductionConfig(Config):
SQLALCHEMY_DATABASE_URI = os.environ.get('DATABASE_URL') or \
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
config = {
'development': DevelopmentConfig, 'testing': TestingConfig, 'production': ProductionConfig,
'default': DevelopmentConfig }
- 為了讓配置方式更靈活且更安全,某些配置可以從環(huán)境變量中導(dǎo)入。例如,SECRET_KEY 的值, 這是個敏感信息,可以在環(huán)境中設(shè)定,但系統(tǒng)也提供了一個默認(rèn)值,以防環(huán)境中沒有定義。
- 配置類可以定義 init_app() 類方法,其參數(shù)是程序?qū)嵗?/li>
2. 藍(lán)本blueprint
- 藍(lán)本和程序類似,也可以定義路由。不同的 是,在藍(lán)本中定義的路由處于休眠狀態(tài),直到藍(lán)本注冊到程序上后,路由才真正成為程序 的一部分。使用位于全局作用域中的藍(lán)本時,定義路由的方法幾乎和單腳本程序一樣
- 藍(lán)本可以在單個文件中定義,也可使用更結(jié)構(gòu)化的方式在包中的多個模塊中創(chuàng)建
from flask import Blueprint
main = Blueprint('main', __name__)
from . import views, errors
- 程序的路由保存在包里的 app/main/views.py 模塊中,而錯誤處理程序保存在 app/main/ errors.py 模塊中。導(dǎo)入這兩個模塊就能把路由和錯誤處理程序與藍(lán)本關(guān)聯(lián)起來
參考鏈接:
Flask 文檔

