1 初識Flask


1.1 搭建開發環境


  1. 用pip+virtualenv管理包和虛擬環境
  2. 這里個人推薦使用conda環境和包管理工具(MiniConda)
  3. Pipenv
    一個程序通常會使用很多的Python包,即依賴(dependency)。而程序不僅僅會在一臺電腦上運行,程序部署上線時需要安裝到遠程服務 器上,而你也許會把它分享給朋友。如果你打算開源的話,就可能會有 更多的人需要在他們的電腦上運行。為了能順利運行程序,他們不得不記下所有依賴包,然后使用pip、conda或Pipenv安裝,這些重復無用的工作當然應該避免。在以前我們通常使用pip搭配一個requirements.txt文件來記錄依賴。但requirements.txt需要手動維護,在使用上不夠靈活。Pipfile的出現就是為了替代難于管理的requirements.txt。

安裝flask,這些包管理工具同時也會安裝flask的相關依賴包


1.2.1 創建程序實例


以下app.py腳本中包含一個最小的Flask程序
對于簡單的的程序來說,程序的主模塊一般命令為app.py。你也可以 使用其他名稱,比如hello.py,但是要避免使用flask.py,因為這和Flask 本身沖突。

#app.py
from flask import Flask 
app = Flask(__name__)  #創建Flask程序實例
#Flask類是Flask的核心類,它提供了很多與程序相關的屬性和方法。

@app.route('/') 
def index():    
       return '<h1>Hello Flask!</h1>'

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

1.2.2 注冊路由

在一個Web應用里,客戶端和服務器上的Flask程序的交互可以簡單 概括為以下幾步:
1)用戶在瀏覽器輸入URL訪問某個資源。
2)Flask接收用戶請求并分析請求的URL。
3)為這個URL找到對應的處理函數。
4)執行函數并生成響應,返回給瀏覽器。
5)瀏覽器接收并解析響應,將信息顯示在頁面中。

上面這些步驟大部分都由Flask完成,我們要做的是建立處理請求的函數,并為其定義對應的URL規則。只需為函數附加 app.route()裝飾器,并傳入URL規則作為參數,我們就可以讓URL與函數建立關聯。這個過程我們稱為注冊路由(route),路由負責管理 URL和函數之間的映射,而這個函數則被稱為視圖函數(view function)。

@app.route('/') 
def index():    
    return '<h1>Hello, World!</h1>'

在這個程序里,app.route()裝飾器把根地址/和index()函數綁定起來,當用戶訪問這個URL時就會觸發index()函數。這個視圖函數可以像其他普通函數一樣執行任意操作,比如從數據庫中獲取信息, 獲取請求信息,對用戶輸入的數據進行計算和處理等。最后,視圖函數 返回的值將作為響應的主體,一般來說,響應的主體就是呈現在瀏覽器窗口的HTML頁面。

雖然這個程序相當簡單,但它卻是大部分Flask程序的基本模式。在復雜的程序中,我們會有許多個視圖函數分別處理不同URL的請求,在視圖函數中會完成更多的工作,并且返回包含各種鏈接、表單、圖片的 HTML文件,而不僅僅是一行字符串。返回的頁面中的鏈接又會指向其 他URL,被單擊后觸發對應的視圖函數,獲得不同的返回值,從而顯示 不同的頁面,這就是我們瀏覽網頁時的體驗。

route()裝飾器的第一個參數是URL規則,用字符串表示,必須以 斜杠(/)開始。這里的URL是相對URL(又稱為內部URL),即不包含域名的URL

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

@app.route('/hi') 
@app.route('/hello') 
def say_hello():    
    return '<h1>Hello, Flask!</h1>'

**2 動態URL **

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

@app.route('/greet/<name>') 
def greet(name):    
    return '<h1>Hello, %s!</h1>' % name
  《多個參數怎么操作呢???》

因為URL中可以包含變量,所以我們將傳入app.route()的字符串稱為URL規則而不是URL。Flask會解析請求并把請求的URL與視圖函數的URL規則進行匹配。比如,這個greet視圖的URL規則 為/greet/<name>,那么類似/greet/foo、/greet/bar的請求都會觸發這個視 圖函數。

當URL規則中包含變量時,如果用戶訪問的URL中沒有添加變量, 比如/greet,那么Flask在匹配失敗后會返回一個404錯誤響應。一個很常見的行為是在app.route()裝飾器里使用defaults參數設置URL變量的默 認值,這個參數接收字典作為輸入,存儲URL變量和默認值的映射。在下面的代碼中,我們為greet視圖新添加了一個app.route()裝飾器,

  @app.route('/greet', defaults={'name': 'Programmer'}) 
  @app.route('/greet/<name>') 
  def greet(name):    
    return '<h1>Hello, %s!</h1>' % name

這時如果用戶訪問/greet,那么變量name會使用默認值 Programmer,視圖函數返回<h1>Hello,Programmer!</h1>。上面的用 法實際效果等同于:

@app.route('/greet') 
@app.route('/greet/<name>') 
def greet(name='Programmer'):   
    return '<h1>Hello, %s!</h1>' % name

1.3 啟動開發服務器


Flask內置了一個簡單的開發服務器(由依賴包Werkzeug提供), 足夠在開發和測試階段使用。在生產環境需要使用性能夠好的生產服務器,以提升安全和性能。

Flask通過依賴包Click內置了一個CLI(Command Line Interface,命 令行交互界面)系統。當我們安裝Flask后,會自動添加一個flask命令腳本,我們可以通過flask命令執行內置命令、擴展提供的命令或是我們自己定義的命令。其中,flask run命令用來啟動內置的開發服務器。啟動時確保執行命令前激活了虛擬環境。

flask run命令運行的開發服務器默認會監聽http://127.0.0.1:5000/地址(按Crtl+C退出),并開啟多線程支持。http://localhost::5000/與http://127.0.0.1:5000/除了地址不同外,兩者沒有實際區別,即域名和IP地址的映射關系。

舊的啟動開發服務器的方式是使用app.run()方法,目前已不推薦 使用(deprecated)。

一般來說,在執行flask run命令運行程序前,我們需要提供程序實例所在模塊的位置。我們在上面可以直接運行程序,是因為Flask會自動 探測程序實例,自動探測存在下面這些規則:
·從當前目錄尋找app.py和wsgi.py模塊,并從中尋找名為app或application的程序實例。
·從環境變量FLASK_APP對應的值尋找名為app或application的程序實例。

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

#Linux或macOS系統使用export命令
 export FLASK_APP=hello
 
 #在Windows系統中使用set命令:
set FLASK_APP=hello

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

 flask run --host=0.0.0.0

這會讓服務器監聽所有外部請求。個人計算機(主機)一般沒有公 網IP(公有地址),所以你的程序只能被局域網內的其他用戶通過你的 個人計算機的內網IP(私有地址)訪問,比如你的內網IP為 192.168.191.1。當局域網內的其他用戶訪問http://192.168.191.1:5000時, 也會看到瀏覽器里顯示一行“Hello,Flask!”。要讓互聯網上的所有人都可訪問,可以考慮使用ngrok(https://ngrok.com/)Localtunnel(https://localtunnel.github.io/www/)等內網穿透/端口轉發工 具。

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

 flask run --port=8000

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

** 設置運行環境**

開發環境是指我們在本地編寫和測試程序時的計算機環境,而生產環境與開發環境相對,它指的是網站部署上線供用戶訪問時的服務器環境。
根據運行環境的不同,Flask程序、擴展以及其他程序會改變相應的行為和設置。為了區分程序運行環境,Flask提供了一個FLASK_ENV環境變量用來設置環境,默認為production(生產)。在開發時,我們可 以將其設為development(開發),這會開啟所有支持開發的特性

在開發環境下,調試模式(Debug Mode)將被開啟,這時執行flask run啟動程序會自動激活Werkzeug內置的調試器(debugger)和重載器 (reloader),它們會為開發帶來很大的幫助。如果你想單獨控制調試模式的開關,可以通過FLASK_DEBUG環境 變量設置,設為1則開啟,設為0則關閉,不過通常不推薦手動設置這個
值。

在生產環境中部署程序時,絕不能開啟調試模式。盡管PIN碼可以 避免用戶任意執行代碼,提高攻擊者利用調試器的難度,但并不能確保 調試器完全安全,會帶來巨大的安全隱患。而且攻擊者可能會通過調試 信息獲取你的數據庫結構等容易帶來安全問題的信息。另一方面,調試 界面顯示的錯誤信息也會讓普通用戶感到困惑。

1.調試器
Werkzeug提供的調試器非常強大,當程序出錯時,我們可以在網頁 上看到詳細的錯誤追蹤信息,這在調試錯誤時非常有用。

2.重載器
當我們對代碼做了修改后,期望的行為是這些改動立刻作用到程序 上。重載器的作用就是監測文件變動,然后重新啟動開發服務器。默認會使用Werkzeug內置的stat重載器,它的缺點是耗電較嚴重, 而且準確性一般。為了獲得更優秀的體驗,我們可以安裝另一個用于監 測文件變動的Python庫Watchdog,安裝后Werkzeug會自動使用它來監測 文件變動

此外如果項目中使用了單獨的CSS或JavaScript文件時,那么瀏覽器可能會緩存這些文件,從而導致對文件做出的修改不能立刻生效。在瀏覽器中,我們可以按下Crtl+F5或Shift+F5執行硬重載(hard reload),即忽略緩存并重載(刷新)頁面。


1.5 Flask擴展


我們將會接觸到很多Flask擴展。擴展(extension)即使用 Flask提供的API接口編寫的Python庫,可以為Flask程序添加各種各樣的功能。大部分Flask擴展用來集成其他庫,作為Flask和其他庫之間的薄薄一層膠水。因為Flask擴展的編寫有一些約定,所以初始化的過程大致相似。大部分擴展都會提供一個擴展類,實例化這個類,并傳入我們創建的程序實例app作為參數,即可完成初始化過程。通常,擴展會在傳 入的程序實例上注冊一些處理函數,并加載一些配置。

在日常開發中,大多數情況下,我們沒有必要重復制造輪子,所以選用擴展可以避免讓項目變得臃腫和復雜。盡管使用擴展可以簡化操作,快速集成某個功能,但同時也會降低靈活性。如果過度使用擴展, 在不需要的地方引入,那么相應也會導致代碼不容易維護。更糟糕的是,質量差的擴展可能還會帶來潛在的Bug,而不同擴展之間也可能會出現沖突。因此,在編寫程序時,應該盡量從實際需求出發,只在需要的時候使用擴展,并把擴展的質量和兼容性作為考慮因素,盡量在效率和靈活性之間達到平衡。


1.6 項目配置


在很多情況下,你需要設置程序的某些行為,這時你就需要使用配置變量。在Flask中,配置變量就是一些大寫形式的Python變量,你也可 以稱之為配置參數或配置鍵。使用統一的配置變量可以避免在程序中以硬編碼(hard coded)的形式設置程序。

在一個項目中,你會用到許多配置:Flask提供的配置,擴展提供的配置,還有程序特定的配置。和平時使用變量不同,這些配置變量都通過Flask對象的app.config屬性作為統一的接口來設置和獲取,它指向的 Config類實際上是字典的子類,所以你可以像操作其他字典一樣操作 它。
Flask提供了很多種方式來加載配置。比如,你可以像在字典中添加 一個鍵值對一樣來設置一個配置:

app.config['ADMIN_NAME'] = 'Peter'
#配置的名稱必須是全大寫形式,小寫的變量將不會被讀取。

使用update()方法則可以一次加載多個值:
app.config.update(    TESTING=True, 
                            SECRET_KEY='_5#yF4Q8z\n\xec]/' )

除此之外,你還可以把配置變量存儲在單獨的Python腳本、JSON 格式的文件或是Python類中,config對象提供了相應的方法來導入配 置,具體我們會在后面了解。

#和操作字典一樣,讀取一個配置就是從config字典里通過將配置變 量的名稱作為鍵讀取對應的值:
value = app.config['ADMIN_NAME']


1.7 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 greet(name):    
    return 'Hello %s!' % 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://localhost:5000/hello


1.8 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, Human!')

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


1.9 模板與靜態文件


一個完整的網站需要使用模板(template)和靜態文件(static file)來生成內容豐富的網頁。 模板即包含程序頁面的HTML文件,靜態文件則是需要在HTML文件中加載的CSS、JavaScript文件,以及圖片、字體文件等資源文件。默認情況下,模板文件存放在項目根目錄中的templates文件夾中,靜態文件存放在static文件夾下,這兩個文件夾需要和包含程序實例的模塊處于同一個目錄下,對應的項目結構示例如下所示:

hello/   
    - templates/   
    - static/    
    - app.py

在開發Flask程序時,使用CSS框架和JavaScript庫是很常見的需求,一般會直接從CDN加載資源或者下載至本地作為本地資源使用。建議在開發環境下自己下載到static目錄下使用本地資源,這樣可以提高加載速度,方便統一管理,出于方便的考慮也可以使用擴展內置的本地資源。在過渡到生產環境時,自己手動管理所有本地資源或自己設置CDN,避免使用擴展內置的資源。這個建議主要基于如下考慮因素:
·鑒于國內的網絡狀況,擴展默認使用的國外CDN可能會無法訪問或訪問過慢。
·不同擴展內置的加載方法可能會加載重復的依賴資源,比如 jQuery。
·在生產環境下,將靜態文件集中在一起更方便管理。
·擴展內置的資源可能會出現版本過舊的情況。
·也存在資源鏈接失效的情況

CDN指分布式服務器系統。服務商把你需要的資源存儲在分布于不 同地理位置的多個服務器,它會根據用戶的地理位置來就近分配服務器 提供服務(服務器越近,資源傳送就越快)。使用CDN服務可以加快網頁資源的加載速度,從而優化用戶體驗。對于開源的CSS和JavaScript 庫,CDN提供商通常會免費提供服務。


1.10 Flask與MVC架構


MVC架構最初是用來設計桌面程序的,后來也被用于Web程序,應 用了這種架構的Web框架有Django、Ruby on Rails等。在MVC架構中, 程序被分為三個組件:數據處理(Model)、用戶界面(View)、交互邏輯(Controller)。如果套用MVC架構的內容,那么Flask中視圖函數的名稱其實并不嚴謹,使用控制器函數(Controller Function)似乎更合適些,雖然它也附帶處理用戶界面。嚴格來說,Flask并不是MVC架構的框架,因為它沒有內置數據模型支持。
粗略歸類,如果想要使用Flask來編寫一個MVC架構的程序,那么視圖函數可以作為控制器(Controller),視圖(View)則是使用Jinja2渲染的HTML模板,而模型(Model)可以使用其他庫來實現,比如SQLAlchemy來創建數據庫模型。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。