一篇文章入門Flask

Flask本身相當於一個內核,其他幾乎所有的功能都要用到擴展(郵件擴展Flask-Mail,用戶認證Flask-Login),都需要用第三方的擴展來實現。

Flask對WSGI (路由)的實現,是採用 Werkzeug,而模板引擎(業務視圖) 則使用 Jinja2。這兩個是Flask框架的核心。

Flask核心就是作爲一個Webapp框架的兩個基礎部分:

  • CGI (WSGI) 路由:是由Werkzeug實現,即將網絡請求翻譯成Python語言
  • Template 業務模版:是由Python流行的Jinja2實現

除此之外,Flask其它一切的都是由第三方插件實現:
包括:

Flask-SQLalchemy:操作數據庫; Flask-migrate:管理遷移數據庫; Flask-Mail:郵件; Flask-WTF:表單; Flask-Bable:提供國際化和本地化支持,翻譯; Flask-script:插入腳本; Flask-Login:認證用戶狀態; Flask-OpenID:認證; Flask-RESTful:開發REST API的工具; Flask-Bootstrap:集成前端Twitter Bootstrap框架; Flask-Moment:本地化日期和時間; Flask-Admin:簡單而可擴展的管理接口的框架

參考Flask擴展列表:http://flask.pocoo.org/extens...

Flask安裝

安裝很簡單:

$ pip install flask

建議在Virtualenv虛擬環境下安裝,因爲Flask需要一系列的依賴,最好給Flask生成一個專用的生產環境,並生產requirement.txt依賴列表:

# 生產虛擬環境
$ virtualenv ./flask_env
# 啓動虛擬環境
$ ./flask_env/bin/active
# 生產依賴列表
$ pip freeze > requirements.txt

Hello World

一個最簡單的Flask程序,只需要三步:

  • 生成Flask類的實例,即一個APP
  • 指定目錄的路由規則,即不同路徑可以實現不同的操作。
  • 運行app:app.run()

hello-world.py

from flask import Flask

# 生產一個Flask APP 實例,並指向當前文件(模塊)
app = Flask(__name__)

# 指定"/"根目錄的路由規則
@app.route('/')
def index():
    return 'Hello World'

# 開始運行app
app.run()

Flask中,路由的實現是用裝飾器:@app.route("/")這種方式來做到的。

Flask路由規則

頁面顯示指定內容:

@app.route('/')
def index():
    return '<h1> 歡迎訪問此頁面 </h1>'

頁面返回指定內容、狀態碼、headers等:

@app.route('/')
def index():
    body = '<h1> 歡迎訪問此頁面 </h1>'
    status_code = 200
    headers = {'Cache-Control':'no-cache', 'Connection':'keep-alive'}
    return body, status_code, headers

指定接收的請求方式

@app.route('/', method='GET')
# ...

給路由傳參數:

@app.route('/orders/<order_id>')
def hello_itheima(order_id):
    return 'The ID of order is: %d' % order_id

限制路由參數的數據類型:

@app.route('/orders/<int:order_id>')
# ...

其中int:order_id是指定參數order_id必須能轉換成int整數,否則這個請求就會自動被拒絕。
比如用戶請求http://xyz.com/orders/HAHAHAH,這就不成功。而http://xyz.com/orders/210這樣的就成功。
image

中斷請求:

# ...

from flask import abort

@app.route('/wrong-page')
def hello():
    # 中斷請求,並返回404狀態碼
    abort(404)

處理錯誤請求:

# ...

@app.errorhandler(404)
def handel_404_error():
    return '<h1> The page does not exist </h1>'

其中,如果路由中調用了abort(404)中斷函數,或是其它產生404錯誤的方法,這個app.errorhandler都會被自動調用。

返回“渲染”過的模版(即把動態的模版渲染成靜態的HTML):

#...
from flask import render_template

@app.route('/')
def index():
    return render_template('index.html')

Flask 路由分割

如果所有路徑的路由都定義在一個文件裏,會很難維護,所以必定要把各個路徑的路由分拆到不同的文件裏。

這種分拆很簡單:

正常情況下,在index.py主模塊中,我們還是一樣正常的定義路由:

#...
@app.route('/')
def index():
   pass

然後我們可以把其它路由的處理函數分別放在別的文件裏,比如:
register.py中定義“普通函數”:

def reg():
    pass

以及login.py中定義一個普通函數:

def signin():
    pass

然後回到主模塊index.py中,我們可以導入這些函數,並顯式的將這些函數註冊到路由上:

from flask import Flask

from register import reg
from login import signin

app = Flask(__name__)

app.route('/register')(reg)
app.route('/login')(signin)

@app.route('/')
def index():
    pass

然後我們可以用app.url_map獲得當前定義過的所有路由:

print( app.url_map )

image

Flask Blueprint 藍圖

我們手動分割路由處理函數,然後分別導入,這樣雖然也簡單,但是不不好的地方是,主模塊外定義的各個處理函數,本身很難看出來處理的是什麼路由邏輯。

爲此,Flask提供了另一種路由分割的方法:即Blueprint類。
而這個Blueprint類生成的對象,是在子模塊中代替了之前我們所使用的Flask類生成的app對象。
也就是說:主模塊還是用app,但是子模塊中用藍圖blueprint。

假設我們現在有一個子模塊order.py定義"/order"路徑的路由,那麼文件中定義如下:

from flask import Blueprint

# 生成藍圖實例:參數中一個是藍圖名稱,一個是主模塊名稱
app_orders = Bluepint('blueprint_orders', __name__)

# 將路由添加到藍圖裏
@app_orders.route('/orders')
def get_orders():
    pass

然後回到主模塊index.py中,把藍圖註冊到主路由上:

#...
from orders import app_orders

app = Flask(__name__)

app.register_blueprint( app_orders )

#...

Hooks 鉤子事件

Flask提供一個完整請求至迴應的事件流,其中包括:

  • @app.before_first_request: 接受第一次請求之前執行
  • @app.before_request: 接受請求前,每次請求之前都執行。
  • @app.route(): 處理請求
  • @app.after_request: 請求之後執行,但前提是請求中沒有出現異常

-@app.teardown_request: 關閉請求時,即每次請求是否異常都會被執行

以下是鉤子的用法:

#...

@app.before_first_request
def handle_before_first_request():
    pass

@app.route('/')
def index():
    pass

@app....
def ...

Flask上下文請求對象 flask.current_app

request.current_app 是Flask特有的一種request請求處理方式,不同於flask.request對象的處理方式,它是能區分多個請求的。

在我們常用的flask.request對象中,會有一個很嚴重的問題:即它是一個全局變量。也就是說,如果服務器在處理併發請求時使用的是在同一個進程裏的多線程,那麼不同用戶的請求也許會使用同一個flask.request對象!這時候request中的請求信息就會出現混淆!

所以Flask引入了request.current_app這個對象,即它能夠根據上下文來區分不同人的請求。
這是怎麼做到的呢?其實很簡單,它只是把request變爲一個局部變量而已。這樣一來,每次的request請求,都是各自獨立的局部對象。

返回響應信息 flask.make_response

除了我們自己定義返回的信息外,Flask提供了一個內置的make_response對象,便於處理返回信息。

返回全文信息:

from flask import make_response

@app.route('/')
def index():
    resp = make_response('<h1> 歡迎訪問此頁面 </h1>')
    resp.status = 200
    resp.headers['Cache-Control'] = 'no-cache'
    return resp

設置cookies:

from flask import make_response

@app.route('/')
def index():
    resp = make_response('<h1> 此頁面會設置你的cookies :) </h1>')
    resp.set_cookie('uuid', '1230sfjdlsj3uu')
    resp.set_cookie('name', 'Jason', max_age=360)
    return resp

其中,max_age是cookie的存活時間,以s秒爲單位。不設置的話,默認是臨時cookies,即瀏覽器關閉後立馬失效。

刪除cookie:resp.delete_cookie('uuid')。注意,這裏的刪除並不是立馬刪除瀏覽器中用戶的cookie,而只是把max_age設置爲0,即瀏覽器關閉後立馬失效。

獲取請求信息 flask.request

Flask中有一個request對象,接收了一切對當前模塊的請求數據。
使用的話,直接在@app.route後面的函數中用def index(request)接收來自裝飾器的請求對象即可使用。

request參數類型:

image

常用的各種類型操作如下:

from flask import request 
app = Flask(__name__)

@app.route('/', method='POST')
def index(request):
    # Get uploaded file
    afile = request.files.get('pic')
    with open('./pic.jpg', 'w') as f:
        f.write( afile.read() )

    # Get a form
    form = request.from    # Dict類型
    name = form.get('name')
    age = form.get('age')

    # Get cookies
    uuid = request.cookies.get('uuid')
    name = request.cookies.get('name')

會話處理 flask.session

在登錄頁設置session,並在index頁根據session判斷是否登錄:

from flask import session
#...

app.config['SECRET_KEY'] = 'asdlkjflaj23jrsdjf任意字符串作爲密鑰kaljdsl;fkja;j'

@app.route('/login')
def login():
    # 設置sessions
    session['uuid'] = '123abadsf'
    return '<p> 登錄成功 </p>'

@app.route('/')
def index():
    # 獲取sessions
    uuid = session['uuid']
    # 判別session是否存在
    if uuid:
        return '<p> 之前已登錄過 </p>'
    else:
        return '<p> 未登錄,請重新登錄 </p>'

其中,Flask默認情況下,會利用app.config['SECRET_KEY'] 的值作爲一個密鑰,來加密你手動設置的session,然後把這個信息轉換爲名叫session的cookie存在瀏覽器中。
這個是Flask特別的一點。

但是把敏感的session數據保存到誰都能訪問的cookie中,即使加密了也不是很安全。
所以一般我們還是會手動把session數據存到服務器後臺的數據庫中,而不是存到cookie中。
每次驗證再與數據庫進行對比。

表單處理 request.form

動態網頁必須要的就是Form表單。Flask中有自帶的form表單處理方法。不過我們也可以用第三方插件Flask-WTF實現。

這裏我們先只講自帶的處理方式。

Flask自帶表單處理

假設我們有一個表單模版form.html

<form method="post">

    用戶名:<input type="text" name="username">
    密碼: <input type="password" name="password">
    確認密碼: <input type="password" name="password2">

    <input type="submit" value="提交"><br>

    {% for message in get_flashed_messages() %}
        {{ message }}
    {% endfor %}
    
</form>

當用戶點擊submit提交時,
整個form信息就會用POST方式提交到Flask的路由文件abc.py中。
我們進行處理如下:

from flask import Flask
from flask import render_template
from flask import request

app.secret_key = 'abc123'

@app.route('/', methods=['GET', 'POST'])
def hello():

    if request.method == 'POST':

        # 獲取參數, 並效驗參數完整性, 如果有問題就進行flash
        username = request.form.get('username')
        password = request.form.get('password')
        password2 = request.form.get('password2')

        if not all([username, password, password2]):
            flash('params error')
        elif password != password2:
            flash('password error')
        else:
            print username
            return 'success'

    return render_template('Congratulations.html')

Flask的HTTP Server

一般我們在開發調試過程中,可以用Flask自帶的WSGI和一個小HTTP Server來實現整個App正常運轉。
但是生產環境中,這兩個自帶的組件就效率很低了。所以我們需要用效率更高的獨立的CGI和獨立的HTTP Server服務器來部署真正的生產環境

一般常見的選項有:

  • HTTP Server -> 首推Nginx
  • CGI翻譯器 -> Gunicorn (Python開發,實現了WSGI翻譯)

所以,我們一般採用Nginx + Gunicorn + Flask來部署網絡應用。

Gunicorn的使用:

# 安裝
$ pip install gunicorn

# 進入Flask app的主目錄
cd ./myFlask

# 用gunicorn服務器啓動Flask app
$ gunicorn -w 4 -b 127.0.0.1:8080 main:app

這個時候,flask就在gunicorn的HTTP服務器上運行了,可以通過127.0.0.1:8080訪問到app。

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