初見Flask

flask簡述

Flask 是一個基於 Python 開發並且依賴 jinja2 模板和 Werkzeug WSGI 服務的一個微型框架,對於 Werkzeug 本質是 Socket 服務端,其用於接收 http 請求並對請求進行預處理,然後觸發 Flask 框架,開發人員基於 Flask 框架提供的功能對請求進行相應的處理,並返回給用戶,如果要返回給用戶複雜的內容時,需要藉助 jinja2 模板來實現對模板的處理,將模板和數據進行渲染,將渲染後的字符串返回給用戶瀏覽器。

“微” (micro) 並不表示你需要把整個 Web 應用塞進單個 Python 文件(雖然確實可以),也不意味着 Flask 在功能上有所欠缺。微框架中的“微”意味着 Flask 旨在保持核心簡單而易於擴展。

默認情況下,Flask 不包含數據庫抽象層、表單驗證,或是其它任何已有多種庫可以勝任的功能。然而,Flask 支持用擴展來給應用添加這些功能,如同是 Flask 本身實現的一樣。衆多的擴展提供了數據庫集成、表單驗證、上傳處理、各種各樣的開放認證技術等功能。Flask 也許是“微小”的,但它已準備好在需求繁雜的生產環境中投入使用。

搭建開發環境

pipenv 是基於 pip 的 Python 的包管理工具,它和 pip 的用法很相似,可以看作是 pip 的加強版,它的出現解決了舊的 pip+virtualenv+requirements.txt 的工作方式的弊端。可以說 pipenv 是 pip、pipfile、virtualenv 的結合體,它讓包安裝、包依賴管理和虛擬環境管理更加方便,使用它可以實現高效的 Python 項目開發工作流。

安裝

在 Linux 或 macOS 系統中使用 sudo 以全局安裝:

sudo pip install pipenv 

查看pipenv版本號:

pipenv --version

創建虛擬環境

在 Python 中,虛擬環境(virtual enviroment)就是隔離的 Python 解釋器環境。通過創建虛擬環境,你可以擁有一個獨立的 Python 解釋器環境。這樣做的好處是可以爲每一個項目創建獨立的Python解釋器環境,因爲不同的項目常常會依賴不同版本的庫或 Python 版本。使用虛擬環境可以保持全局 Python 解釋器環境的乾淨,避免包和版本的混亂,並且可以方便地區分和記錄每個項目的依賴,以便在新環境下復現依賴環境。
用以下命令爲當前的項目創建虛擬環境:

pipenv install

虛擬環境的目錄

這會爲當前項目創建一個文件夾,其中包含隔離的 Python 解釋器環境,並且安裝 pip、wheel、setuptools 等基本的包。如果是下載的已經寫好的程序,並且裏面包含 Pipfile 文件,那麼這個文件中列出的依賴包,會在這個命令執行時一併被安裝。默認情況下,Pipenv 會統一管理所有虛擬環境。在 Windows 系統中,虛擬環境文件夾會在 C:\Users\Administrator\.virtualenvs\ 目錄下創建;在 Linux 或 macOS 會在 ~/.local/share/virtualenvs/ 目錄下創建。如果你想在項目目錄內創建虛擬環境文件夾,可以設置環境變量

PIPENV_VENV_IN_PROJECT = True

這時名爲 .venv 的虛擬環境文件夾將在項目根目錄被創建。當然我們可以通過--three和--two選項來聲明虛擬環境中使用的 Python 版本(分別對應Python3和Python2),或是使用--python選項指定具體的版本號。同時要確保對應版本的Python已經安裝在電腦中。用以下命令顯示激活虛擬環境:

pipenv shell

當執行 pipenv shellpipenv run 命令時,Pipenv 會自動從項目目錄下的 .env 文件中加載環境變量。Pipenv會啓動一個激活虛擬環境的子 shell ,現在你會發現命令行提示符前添加了虛擬環境名“(虛擬環境名稱)$”,比如:

(helloflask-5Pa0ZfZw) $

這說明我們已經成功激活了虛擬環境,現在你的所有命令都會在虛擬環境中執行。當你需要退出虛擬環境時,使用 exit 命令。除了顯式地激活虛擬環境,Pipenv 還提供了一個神奇的命令,允許你不顯式激活虛擬環境即可在當前項目的虛擬環境中執行命令,比如:

pipenv run python hello.py

這會使用虛擬環境中的Python解釋器,而不是全局的Python解釋器。事實上,和顯式激活/關閉虛擬環境的傳統方式相比,pipenv run是更推薦的做法,因爲這個命令可以讓你在執行操作時不用關心自己是否激活了虛擬環境。

依賴管理

在創建虛擬環境時,如果項目根目錄下沒有 Pipfile 文件,pipenv install 命令還會在項目文件夾根目錄下創建PipfilePipfile.lock 文件,前者用來記錄項目依賴包列表,而後者記錄了固定版本的詳細依賴包列表。當我們使用 Pipen 安裝/刪除/更新依賴包時,Pipfile 以及 Pipfile.lock 會自動更新。你可以使用 pipenv graph 命令查看當前環境下的依賴情況,或是在虛擬環境中使用 pip list 命令查看依賴列表。當需要在一個新的環境運行程序時,只需要執行 pipenv install 命令。Pipenv 就會創建一個新的虛擬環境,然後自動從 Pipfile 中讀取依賴並安裝到新創建的虛擬環境中。

Pipenv 會自動幫我們管理虛擬環境,所以在執行 pipenv install 安裝 Python 包時,無論是否激活虛擬環境,包都會安裝到虛擬環境中。後面我們都將使用 Pipenv 安裝包,這相當於在激活虛擬環境的情況下使用 pip 安裝包。只有需要在全局環境下安裝/更新/刪除包,我們纔會使用pip。

創建 flask 程序

最小的flask程序在此

from flask import Flask
app  = Flask(__name__)

@app.route('/')
def hello():
    return "<h1>hello flask</h1>"

創建程序實例

from flask import Flask
app = Flask(__name__)

傳入構造方法的第一個參數是模塊或包的名稱,我們應該使用特殊變量 __name__ 。Python 會根據所處的模塊來賦予 name 變量相應的值,對於我們的程序來說(app.py),這個值爲 app 。除此之外,這也會幫助 Flask 在相應的文件夾裏找到需要的資源,比如模板和靜態文件。

註冊路由

@app.route('/')
def hello():
    return "<h1>hello flask</h1>"

在一個 Web 應用裏,客戶端和服務器上的 Flask 程序的交互可以簡單概括爲以下幾步:

  1. 用戶在瀏覽器輸入 URL 訪問某個資源。
  2. Flask 接收用戶請求並分析請求的 URL。
  3. 爲這個 URL 找到對應的處理函數。
  4. 執行函數並生成響應,返回給瀏覽器。
  5. 瀏覽器接收並解析響應,將信息顯示在頁面中。

在上面這些步驟中,大部分都由 Flask 完成,我們要做的只是建立處理請求的函數,併爲其定義對應的 URL 規則。只需爲函數附加 app.route() 裝飾器,並傳入 URL 規則作爲參數,我們就可以讓 URL 與函數建立關聯。這個過程我們稱爲註冊路由 (route),路由負責管理 URL 和函數之間的映射,而這個函數則被稱爲視圖函數 (view function)。route() 裝飾器的第一個參數是 URL 規則,用字符串表示,必須以斜槓(/)開始。這裏的 URL 是相對 URL(又稱爲內部 URL ),即不包含域名的 URL。

爲視圖函數綁定多個 URL

一個視圖函數可以綁定多個 URL,比如下面的代碼把 /hi 和 /hello 都綁定到 say_hello() 函數上,這就會爲 say_hello 視圖註冊兩個路由,用戶訪問這兩個 URL 均會觸發 say_hello() 函數,獲得相同的響應。

@app.route('/')
@app.route('/hello')
def index():
    return "hello world"

爲視圖函數綁定動態 URL

在 URL 規則中添加變量部分,使用“<變量名>”的形式表示。Flask 處理請求時會把變量傳入視圖函數,所以我們可以添加參數獲取這個變量值。

@app.route('/greet/<name>')
def index(name):
    return "hello,{}".format(name)

因爲 URL 中可以包含變量,所以我們將傳入 app.route() 的字符串稱爲 URL 規則,而不是 URL。Flask 會解析請求並把請求的URL與視圖函數的URL規則進行匹配。比如,這個 index 視圖的 URL 規則爲 '/greet/<name>',那麼類似 /greet/foo、/greet/bar 的請求都會觸發這個視圖函數。還可以設置默認值,這樣即使不輸入 name 的值訪問也不會 404

啓動開發服務器

Flask 內置了一個簡單的開發服務器(由依賴包 Werkzeug 提供), 足夠在開發和測試階段使用。Flask 通過依賴包 Click 內置了一個 CLI(Command Line Interface,命令行交互界面)系統。當我們安裝 Flask 後,會自動添加一個 flask 命令腳本,我們可以通過 flask 命令執行內置命令、擴展提供的命令或是我們自己定義的命令。其中,flask run 命令用來啓動內置的開發服務器。

pipenv run flask run

自動發現程序

一般來說,在執行 flask run 命令運行程序前,我們需要提供程序實例所在模塊的位置。我們在上面可以直接運行程序,是因爲 Flask 會自動探測程序實例,自動探測存在下面這些規則:

  • 從當前目錄尋找 app.pywsgi.py 模塊,並從中尋找名爲 app 或 application 的程序實例。
  • 從環境變量 FLASK_APP 對應的值尋找名爲 app 或 application 的程序實例。

因爲我們的程序主模塊命名爲 app.py,所以 flask run 命令會自動在其中尋找程序實例。如果你的程序主模塊是其他名稱,比如 hello.py,那麼需要設置環境變量 FLASK_APP,將包含程序實例的模塊名賦值給這個變量。Linux 或 macOS 系統使用 export 命令:

export FLASK_APP=hello

管理環境變量

Flask的自動發現程序實例機制還有第三條規則:如果安裝了 python-dotenv,那麼在使用 flask run 或其他命令時會使用它自動從 .flaskenv 文件和 .env 文件中加載環境變量。當安裝了 python-dotenv 時,Flask 在加載環境變量的優先級是:
手動設置的環境變量>.env 中設置的環境變量>.flaskenv 設置的環境變量。
除了 FLASK_APP,在後面我們還會用到其他環境變量。環境變量在新創建命令行窗口或重啓電腦後就清除了,每次都要重設變量有些麻煩。而且如果你同時開發多個 Flask 程序,這個 FLASK_APP 就需要在不同的值之間切換。爲了避免頻繁設置環境變量,我們可以使用 python-dotenv 管理項目的環境變量,首先使用 Pipenv 將它安裝到虛擬環境:

pipenv install python-dotenv

我們在項目根目錄下分別創建兩個文件:.env 和 .flaskenv。.flaskenv 用來存儲和 Flask 相關的公開環境變量,比如 FLASK_APP;而 .env 用來存儲包含敏感信息的環境變量,比如後面我們會用來配置 Email 服務器的賬戶名與密碼。在 .flaskenv 或 .env 文件中,環境變量使用鍵值對的形式定義,每行一個以#開頭的爲註釋。.env 包含敏感信息,除非是私有項目,否則絕對不能提交到 Git 倉庫中。當你開發一個新項目時,記得把它的名稱添加到 .gitignore 文件中, 這會告訴 Git 忽略這個文件。gitignore 文件是一個名爲 .gitignore 的文本文件,它存儲了項目中 Git 提交時的忽略文件規則清單。Python項目的.gitignore模板

更多啓動選項

使服務器外部可見

在上面啓動的 Web 服務器默認是對外不可見的,可以在 run 命令後添加 --host 選項將主機地址設爲 0.0.0.0 使其對外可見

flask run --host=0.0.0.0

內網穿透工具

改變默認端口

Flask 提供的 Web 服務器默認監聽 5000 端口,你可以在啓動時傳入參數來改變它

flask run --port=8000

執行 flask run 命令時的 host 和 port 選項也可以通過環境變量 FLASK_RUN_HOSTFLASK_RUN_PORT 設置。事實上,Flask 內置的命令都可以使用這種模式定義默認選項值,即 “FLASK_<COMMAND>_<OPTION>”,你可以使用 flask--help 命令查看所有可用的命令。

設置運行環境

開發環境(development enviroment)和生產環境(production enviroment)是我們會頻繁接觸到的概念。
開發環境是指我們在本地編寫和測試程序時的計算機環境,
而生產環境與開發環境相對,它指的是網站部署上線供用戶訪問時的服務器環境。
根據運行環境的不同,Flask 程序、擴展以及其他程序會改變相應的行爲和設置。爲了區分程序運行環境,Flask 提供了一個 FLASK_ENV 環境變量用來設置環境,默認爲 production 。在開發時,我們可以將其設爲 development ,這會開啓所有支持開發的特性。爲了方便管理,我們將把環境變量 FLASK_ENV 的值寫入 .flaskenv 文件中:

FLASK_ENV=development

在開發環境下,調試模式(Debug Mode)將被開啓,這時執行 flask run 啓動程序會自動激活 Werkzeug 內置的調試器(debugger)和重載器(reloader),它們會爲開發帶來很大的幫助。如果你想單獨控制調試模式的開關,可以通過 FLASK_DEBUG 環境變量設置,設爲 1 則開啓,設爲 0 則關閉,不過通常不推薦手動設置這個值。
在生產環境中部署程序時,絕不能開啓調試模式。儘管 PIN 碼可以避免用戶任意執行代碼,提高攻擊者利用調試器的難度,但並不能確保調試器完全安全,會帶來巨大的安全隱患。而且攻擊者可能會通過調試信息獲取你的數據庫結構等容易帶來安全問題的信息。另一方面,調試界面顯示的錯誤信息也會讓普通用戶感到困惑。
當在一個新電腦創建運行環境時,使用 pipenv install 命令時需要添加額外的 --dev 選項纔會安裝 dev-packages 部分定義的開發依賴包。

項目配置

在Flask中,配置變量就是一些大寫形式的 Python 變量,你也可以稱之爲配置參數或配置鍵。使用統一的配置變量可以避免在程序中以硬編碼(hard coded)的形式設置程序。在一個項目中,你會用到許多配置:Flask 提供的配置,擴展提供的配置,還有程序特定的配置。和平時使用變量不同,這些配置變量都通過 Flask 對象的 app.config 屬性作爲統一的接口來設置和獲取,它指向的 Config 類實際上是字典的子類,所以你可以像操作其他字典一樣操作它。

app.config['ADMIN_NAME']='Eric'

配置的名稱必須是全大寫形式,小寫的變量將不會被讀取。使用 update() 方法則可以一次加載多個值:

app.config.update(
    TESTING1=True
    TESTING2=flase
)

和操作字典一樣,讀取一個配置就是從config字典裏通過將配置變量的名稱作爲鍵讀取對應的值:

value = app.config['ADMIN_NAME']

URL與端點

在 Web 程序中,URL 無處不在。如果程序中的 URL 都是以硬編碼的方式寫出,那麼將會大大降低代碼的易用性。比如,當你修改了某個路由的URL 規則,那麼程序裏對應的 URL 都要一個一個進行修改。更好的解決辦法是使用 Flask 提供的 url_for() 函數獲取 URL,當路由中定義的 URL 規則被修改時,這個函數總會返回正確的 URL。調用 url_for() 函數時,第一個參數爲端點(endpoint)值。在 Flask 中,端點用來標記一個視圖函數以及對應的 URL 規則。

@app.route('/')
def index():
    return "hello flask"

這個路由的端點即視圖函數的名稱 index,調用 url_for('index')即可獲取對應的 URL,即“/”。
如果 URL 含有動態部分,那麼我們需要在 url_for() 函數裏傳入相應的參數,以下面的視圖函數爲例:

@app.route('/hello/<name>')
def index(name):
    return "hello {}".format(name)

這時使用 url_for('say_hello',name='Jack')得到的 URL 爲“/hello/Jack”。
我們使用 url_for()函數生成的 URL 是相對 URL(即內部URL), 即URL中的path部分,比如“/hello”,不包含根URL。相對URL只能在程序內部使用。如果你想要生成供外部使用的絕對 URL,可以在使用 url_for()函數時,將 _external 參數設爲 True,這會生成完整的 URL, 比如 http://helloflask.com/hello,在本地運行程序時則會獲得 http://localhost:5000/hello。

Flask 命令

除了 Flask 內置的 flask run 等命令,我們也可以自定義命令。在虛擬環境安裝 Flask 後,包含許多內置命令的 flask 腳本就可以使用了。在前面我們已經接觸了很多 flask 命令,比如運行服務器的 flask run,啓動 shell 的 flask shell。通過創建任意一個函數,併爲其添加 app.cli.command() 裝飾器, 我們就可以註冊一個 flask 命令。hello() 命令函數,在函數中我們仍然只是打印一行問候。

@app.cli.command() 
def hello():
    click.echo("hello,flask")

函數的名稱即爲命令名稱,這裏註冊的命令即 hello,你可以使用 flask hello 命令來觸發函數。作爲替代,你也可以在 app.cli.command() 裝飾器中傳入參數來設置命令名稱,比如 app.cli.command('hello'), 會把命令名稱設置爲hello,完整的命令即flask hello。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章