已經解釋的非常詳細了,我這里只把我關注的點以我的理解記錄一下。
一:程序的基本結構
1. 初始化
2. 路由和視圖函數
- flask使用route裝飾器把一個函數綁定到對應的 URL 上,把修飾的函數注冊為路由
- URL 使用尖括號 <variable_name> , flask將這個部分作為命名參數傳遞到你的函數。可以給該參數定義轉換器實現(xià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 從客戶端收到請求時,要先讓視圖函數能訪問一些對象,這樣才能處理請求。例如request對象封裝了客戶端發(fā)送的 HTTP 請求。
Flask 使用上下文臨時把某些對象變?yōu)槿挚稍L問。
-
在 Flask 中有兩種上下文:程序上下文和請求上下文
flask上下文 Flask 在分發(fā)請求之前激活(或推送)程序和請求上下文,請求處理完成后再將其刪除
在程序實例上調用 app.app_context() 可獲得一個程序上 下文。
Flask 中的某些對象是全局對象,但卻不是通常的那種。這些對象實際上是特定環(huán)境的局部對象的代理。雖然很拗口,但實際上很容易理解。
想象一下處理線程的環(huán)境。一個請求傳入,Web 服務器決定生成一個新線程( 或者別的什么東西,只要這個底層的對象可以勝任并發(fā)系統(tǒng),而不僅僅是線程)。 當 Flask 開始它內部的請求處理時,它認定當前線程是活動的環(huán)境,并綁定當前的應用和 WSGI 環(huán)境到那個環(huán)境上(線程)。它的實現(xiàn)很巧妙,能保證一個應用調用另一個應用時不會出現(xiàn)問題。
所以,這對你來說意味著什么?除非你要做類似單元測試的東西,否則你基本上可以完全無視它。你會發(fā)現(xià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)境管理器。結合 <cite>with</cite> 聲明,綁定一個測試請求,這樣你才能與之交互
4. 請求調度
- 程序收到客戶端發(fā)來的請求時,要找到處理該請求的視圖函數。為了完成這個任務,F(xiàn)lask 會在程序的 URL 映射中查找請求的 URL。URL 映射是 URL 和視圖函數之間的對應關系。 Flask 使用 app.route 修飾器或者非修飾器形式的 app.add_url_rule() 生成映射。
- URL 映射中的 HEAD、Options、GET 是請求方法,由路由進行處理。Flask 為每個路由都指 定了請求方法,這樣不同的請求方法發(fā)送到相同的 URL 上時,會使用不同的視圖函數進 行處理。
5. 請求鉤子
- 有時需要在處理請求之前或之后執(zhí)行代碼。例如,認證發(fā)起請求的用戶。為了避免在每個視圖函數中都使用重復的代碼, Flask 提供了注冊通用函數的功能,注冊的函數可在請求被分發(fā)到視圖函數之前或之后調用。這種程序叫做鉤子函數。
- 請求鉤子使用修飾器實現(xiàn)
- flask支持以下幾種鉤子
- before_first_request:注冊一個函數,在處理第一個請求之前運行
- before_request:注冊一個函數,在每次請求之前運行
- after_request:注冊一個函數,如果沒有未處理的異常拋出,在每次請求之后運行
- teardown_request:注冊一個函數,即使有未處理的異常拋出,也在每次請求之后運行。
- 在請求鉤子函數和視圖函數之間共享數據一般使用上下文全局變量 g,例如,before_ request 處理程序可以從數據庫中加載已登錄用戶,并將其保存到 g.user 中。隨后調用視 圖函數時,視圖函數再使用 g.user 獲取用戶。
6. 響應
- Flask 視圖函數一般返回一個 Response 對象。make_response() 函數可接受 1 個、2 個或 3 個參數(和視圖函數的返回值一樣),并返回一個 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
- 重定向是一種特殊的響應,響應內容是 URL,而不是包含 HTML 代碼的字符串。瀏覽器收到 這種響應時,會向重定向的 URL 發(fā)起 GET 請求,顯示頁面的內容。
- 重定向響應可以使用 3 個值形式的返回值生成,也可在 Response 對象中設定。不過,由于使用頻繁,F(xiàn)lask 提 供了 redirect() 輔助函數,
from flask import redirect
@app.route('/')
def index():
return redirect('http://www.example.com')
- 可以使用redirect() 輔助函數, 生成 HTTP 重定向響應。redirect() 函數的參數是重定向的 URL,推薦使用 url_for() 生成 URL,因為這 個函數使用 URL 映射生成 URL,從而保證 URL 和定義的路由兼容,而且修改路由名字后 依然可用。
- url_for() 函數的第一個且唯一必須指定的參數是端點名,即路由的內部名字。默認情 況下,路由的端點是相應視圖函數的名字。
- 重定向時請求數據會request上下文的內容會丟失,因為重定向相當于另一個請求,這時程序可以把數據存儲在session中,在請求之間“記住”數據。session是一種私有存 儲,存在于每個連接到服務器的客戶端中。默認情況下,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擴展
社區(qū)成員開發(fā)了大量不同用途的flask擴展,下面介紹如何把擴展整合到程序中
- 使用pip安裝flask擴展
- 在程序中使用
from flask.ext.script import Manager
flask.ext模塊導入該擴展
- 專為 Flask 開發(fā)的擴展都暴漏在 flask.ext 命名空間下
二:模版
- 默認情況下,F(xiàn)lask 在程序文件夾中的 templates 子文件夾中尋找模板
- flask支持很多模版引擎,
render_template
函數把Jinja2
模板引擎集成到了程序中 -
render_template
函 數的第一個參數是模板的文件名。隨后的參數都是鍵值對,表示模板中變量對應的真實值
from flask import Flask, render_template # ...
@app.route('/user/<name>')
def user(name):
return render_template('user.html', name=name)
1. 變量
- 在模板中使用的
{{ name }}
結構表示一個變量,它是一種特殊的占位符,告訴模板引擎這個位置的值從渲染模板時使用的數據中獲取 - 可以使用過濾器修改變量,過濾器名添加在變量名之后,中間使用豎線分隔
-
Jinja2 提供的部分常用過濾器:
Jinja2 提供的部分常用過濾器
2. jinja2控制結構
- Jinja2 提供了多種控制結構,可用來改變模板的渲染流程
條件控制語句
{% if user %}
Hello, {{ user }}!
{% else %}
Hello, Stranger!
{% endif %}
for循環(huán)
<ul>
{% for comment in comments %}
<li>{{ comment }}</li> {% endfor %}
</ul>
引入別的文件
{% include 'common.html' %}
- 需要在多處重復使用的模板代碼片段可以寫入單獨的文件,再包含在所有模板中,以避免 重復:
- 模版繼承
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 中的基模板提供了一個網頁框架,引入了 Bootstrap 中的所有 CSS 和JavaScript 文件。
3. 自定義錯誤顯示頁面
- Flask 允許程序使用基于模板的自定義錯誤頁面
4.鏈接
- Flask 提供了 url_for() 輔助函數,它可以使用程序 URL 映射中保存 的信息生成 URL
- url_for() 函數最簡單的用法是以視圖函數名(或者 app.add_url_route() 定義路由時使用 的端點名)作為參數,返回對應的 URL。
- 使用 url_for() 生成動態(tài)地址時,將動態(tài)部分作為關鍵字參數傳入。例如,
url_for ('user', name='john', _external=True) 的返回結果是 http://localhost:5000/user/john。
。傳入 url_for() 的關鍵字參數不僅限于動態(tài)路由中的參數。函數能將任何額外參數添加到 查詢字符串中,例如,url_for('index', page=2) 的返回結果是 /?page=2。
5.靜態(tài)文件
- 默認設置下,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 請求中提交的表單數據。
- Flask-WTF擴展可以把處理 Web 表單的過程變成一 種愉悅的體驗。
1. 跨站請求偽造保護
- 默認情況下,F(xiàn)lask-WTF 能保護所有表單免受跨站請求偽造(CSRF)的攻擊
- 為了實現(xiàn) CSRF 保護,F(xiàn)lask-WTF 需要程序設置一個密鑰。Flask-WTF 使用這個密鑰生成加密令牌,再用令牌驗證請求中表單數據的真?zhèn)?/li>
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
- app.config 字典可用來存儲框架、擴展和程序本身的配置變量。
4.2 表單類
- 類定義表單中的 一組字段,每個字段都用對象表示。字段對象可附屬一個或多個驗證函數。驗證函數用來 驗證用戶提交的輸入值是否符合要求。
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')
//字段構造函數的第一個參數是把表單渲染成 HTML 時使用的標號
//StringField 構造函數中的可選參數 validators 指定一個由驗證函數組成的列表,在接受 用戶提交的數據之前驗證數據。驗證函數 Required() 確保提交的字段不為空
3.把表單渲染成HTML
- 表單字段是可調用的,在模板中調用后會渲染成 HTML。
- Flask-Bootstrap 提供了一個非常高端的輔助函 數,可以使用 Bootstrap 中預先定義好的表單樣式渲染整個 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 %}
- 在視圖函數中處理表單
@app.route('/', methods=['GET', 'POST'])
def index():
name = None
form = NameForm() 在視圖函數中創(chuàng)建一個 NameForm 類實例用于表示表單
if form.validate_on_submit():
name = form.name.data
form.name.data = ''
return render_template('index.html', form=form, name=name)
- Flash消息
- flash() 函數
七:大型程序的結構
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)境變量中導入。例如,SECRET_KEY 的值, 這是個敏感信息,可以在環(huán)境中設定,但系統(tǒng)也提供了一個默認值,以防環(huán)境中沒有定義。
- 配置類可以定義 init_app() 類方法,其參數是程序實例。
2. 藍本blueprint
- 藍本和程序類似,也可以定義路由。不同的 是,在藍本中定義的路由處于休眠狀態(tài),直到藍本注冊到程序上后,路由才真正成為程序 的一部分。使用位于全局作用域中的藍本時,定義路由的方法幾乎和單腳本程序一樣
- 藍本可以在單個文件中定義,也可使用更結構化的方式在包中的多個模塊中創(chuàng)建
from flask import Blueprint
main = Blueprint('main', __name__)
from . import views, errors
- 程序的路由保存在包里的 app/main/views.py 模塊中,而錯誤處理程序保存在 app/main/ errors.py 模塊中。導入這兩個模塊就能把路由和錯誤處理程序與藍本關聯(lián)起來
參考鏈接:
Flask 文檔