1. 程式人生 > >Flask修煉——模板!

Flask修煉——模板!



內容概述:
to_python、to_url,
異常捕獲,
請求鉤子,
獲取請求引數,
狀態保持,
上下文,
Flask-Script 擴充套件,
模板


to_python、to_url

自定義轉換器

to_python: 可以對匹配到的引數進行處理並返回,在呼叫檢視函式前執行

to_url :在使用 url_for 的時候,對檢視函式傳的引數進行處理,處理完畢之後以便能夠進行路由匹配




異常捕獲

HTTP異常主動丟擲

abort 方法:丟擲一個給定狀態程式碼的 HTTPException 或者 指定響應,例如想要用一個頁面未找到異常來終止請求,你可以呼叫 abort(404)。

引數:code——HTTP 的錯誤狀態碼

捕獲錯誤

errorhandler 裝飾器 :註冊一個錯誤處理程式,當程式丟擲指定錯誤狀態碼的時候,就會呼叫該裝飾器所裝飾的方法

引數: code_or_exception – HTTP的錯誤狀態碼或指定異常

from flask import Flask
from flask import abort

app = Flask(__name__)


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


@app.route('/demo1')
def demo1
(): # 主動丟擲 HTTP 指定錯誤程式碼 # abort(404) a = 0 b = 1 / a return 'demo1' @app.errorhandler(404) def page_not_found(error): return "頁面不見了~" @app.errorhandler(ZeroDivisionError) def zero_division_error(error): return "除數不能為0 " if __name__ == '__main__': app.run(debug=True)



請求鉤子

在客戶端和伺服器互動的過程中,有些準備工作或掃尾工作需要處理 ,為了讓每個檢視函式避免編寫重複功能的程式碼,Flask提供了通用設施的功能,即請求鉤子 .

請求鉤子是通過裝飾器的形式實現,Flask支援如下四種請求鉤子:

before_first_request: 在第一次請求之前會執行
before_request : 在每次請求之前會執行,可以在這裡對一些非法的請求進行阻止,那麼檢視函式將不再被呼叫
after_request: 在請求之後會執行,並且函式裡面接收一個引數:響應,還需要將響應進行返回
teardown_request: 在請求之後會執行,如果請求的函式有異常,會把具體異常丟擲

from flask import Flask


app = Flask(__name__)


@app.before_first_request
def before_first_request():
    """在第一次請求之前會訪問該函式"""
    print('before_first_request')


@app.before_request
def before_request():
    """在每次請求之前會訪問該函式"""
    print('before_request')
    # 可以對一些非法的請求進行阻止
    # if 如果ip在黑名單:
    #   return "在黑名單中"


@app.after_request
def after_request(response):
    """在請求之後會呼叫,並且函式裡面接收一個引數:響應,還需要將響應進行返回"""
    print('after_request')
    return response


@app.teardown_request
def teardown_request(error):
    """在請求之後會執行,如果請求的函式報有異常,會把具體異常傳入到此函式"""
    print('teardown_request')


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


if __name__ == '__main__':
    app.run(debug=True)
裝飾器路由的實現

Flask有兩大核心:Werkzeug和Jinja2

- Werkzeug實現路由、除錯和Web伺服器閘道器介面
- Jinja2實現了模板。



獲取請求引數

request:

request 就是flask中代表當前請求的 request 物件,其中一個請求上下文變數(理解成全域性變數,在檢視函式中直接使用可以取到當前本次請求)

常用的屬性如下:

屬性 說明 型別
data 記錄請求的資料,並轉換為字串 *
form 記錄請求中的表單資料 MultiDict
args 記錄請求中的查詢引數 MultiDict
cookies 記錄請求中的cookie資訊 Dict
headers 記錄請求中的報文頭 EnvironHeaders
method 記錄請求使用的HTTP方法 GET/POST
url 記錄請求的URL地址 string
files 記錄請求上傳的檔案 *
from flask import Flask
from flask import request

app = Flask(__name__)


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


# OSError: [Errno 98] Address already in use
# 埠被佔用,是程式沒有完全關閉,後臺還在執行
# 在 ubuntu 中使用命令: netstat -apn  | grep 5000  找到 5000 埠對應的 pid
# 使用 kill -9 pid  命令進行清除


@app.route('/upload', methods=['POST'])
def upload():
    file = request.files.get('pic')
    file.save('aaa.png')
    return 'success'


@app.route('/data', methods=['POST'])
def data():
    data = request.data
    print(data)
    return 'ok'


if __name__ == '__main__':
    app.run(debug=True)



狀態保持

  • http 是一種無狀態協議,瀏覽器請求伺服器是無狀態的。
  • 無狀態:指一次使用者請求時,瀏覽器、伺服器無法知道之前這個使用者做過什麼,每次請求都是一次新的請求。
  • 無狀態原因:瀏覽器與伺服器是使用 socket 套接字進行通訊的,伺服器將請求結果返回給瀏覽器之後,會關閉當前的 socket 連線,而且伺服器也會在處理頁面完畢之後銷燬頁面物件。
  • 實現狀態保持主要有兩種方式:
    • 在客戶端儲存資訊使用Cookie
    • 在伺服器端儲存資訊使用Session

無狀態協議:

  1. 協議對於事務處理沒有記憶能力
  2. 對同一個 url 請求沒有上下文關係
  3. 每次的請求都是獨立的,它的執行情況和結果與前面的請求和之後的請求是無直接關係的,它不會受前面的請求應答情況直接影響,也不會直接影響後面的請求應答情況
  4. 伺服器中沒有儲存客戶端的狀態,客戶端必須每次帶上自己的狀態去請求伺服器
  5. 人生若只如初見

cookie
  • Cookie是儲存在瀏覽器中的一段純文字資訊,建議不要儲存敏感資訊如密碼,因為電腦上的瀏覽器可能被其它人使用
  • Cookie基於域名安全,不同域名的Cookie是不能互相訪問的
    • 瀏覽器的同源策略,所謂同源是指,域名,協議,埠相同。不同源的客戶端腳(javascript、ActionScript)本在沒明確授權的情況下,不能讀寫對方的資源。

make_response 獲取 response 物件

set_cookie(‘username’, ‘xiaohei’, max_age=3600) 設定cookie,設定過期時間

request.cookies.get() 獲取cookie

cookie 流程:

設定 cookie: 通過響應帶給瀏覽器

獲取 cookie:從請求中獲取

  1. 瀏覽器傳送登入請求,傳送請求報文,帶上要登入的賬號和密碼
  2. 伺服器校驗賬號和密碼
  3. 如果正確就給出響應,帶上設定好的 cookie ,response.set_cookie('username', 'xiaohei')
  4. 瀏覽器會自動將 cookie 儲存起來
  5. 下次請求該網站時,會把 cookie 請求帶到伺服器,伺服器取到傳過來的 cookie,根據 cookie 就可以知道是否登入,是誰登入
from flask import Flask
from flask import make_response
from flask import request

app = Flask(__name__)


@app.route('/')
def index():
    user_id = request.cookies.get('user_id')
    user_name = request.cookies.get('user_name')
    return "%s-----%s" % (user_id, user_name)


@app.route('/login')
def login():
    # 預設判斷賬號與密碼是正確的
    response = make_response('success')
    # 設定 cookie
    response.set_cookie('user_id', '1', max_age=3600)
    response.set_cookie('user_name', 'xiaohei', max_age=3600)
    return response


@app.route('/logout')
def logout():
    response = make_response('success')
    response.delete_cookie('user_id')
    response.delete_cookie('user_name')
    return response


if __name__ == '__main__':
    app.run(debug=True)

session
  • 對於敏感、重要的資訊,建議要儲存在伺服器端,不能儲存在瀏覽器中,如使用者名稱、餘額、等級、驗證碼等資訊
  • 在伺服器端進行狀態保持的方案就是Session
  • Session依賴於Cookie
session 流程
  1. 傳送登入請求,傳送請求報文,帶上要登入的賬號和密碼
  2. 伺服器處理請求:校驗密碼,儲存使用者資訊到伺服器,每個使用者資訊對應一個 sid
  3. 伺服器給出相應,把 sid 返回給瀏覽器(cookie)
  4. 瀏覽器把 sid 儲存起來
  5. 下次請求時預設會帶上 sid, 伺服器可以通過請求取到 sid,在通過 sid 取到對應的使用者資訊,取到資訊就可以知道是否登入,是誰登入
from flask import Flask
from flask import session

app = Flask(__name__)
# 使用 session 的話,需要配置 secret_key
app.config['SECRET_KEY'] = 'asdfghj'


@app.route('/')
def index():
    user_id = session.get('user_id', '')
    user_name = session.get('user_name', '')
    return '%s -- %s' % (user_id, user_name)


@app.route('/login')
def login():
    # 假裝校驗成功
    session['user_id'] = "1"
    session['user_name'] = 'xiaohei'
    return 'success'


@app.route('/logout')
def logout():
    session.pop('user_id')
    session.pop('user_name')
    return 'success'


if __name__ == '__main__':
    app.run(debug=True)



上下文

上下文:相當於一個容器,儲存了 Flask 程式執行過程中的一些資訊。

pycharm 中移動整行程式碼的快捷鍵: shift + alt + 上下鍵

請求上下文
# 請求上下文中的變數
from flask import session
from flask import request

request: 封裝了 HTTP 請求的內容,針對的是 http 請求

session:用來記錄請求會話中的資訊,針對的是使用者資訊。

應用上下文

它的字面意思是 應用上下文,但它不是一直存在的,它只是request context 中的一個對 app 的代理(人),所謂local proxy。它的作用主要是幫助 request 獲取當前的應用,它是伴 request 而生,隨 request 而滅的。

# 應用上下文中的變數
from flask import current_app
from flask import g

current_app: 應用程式上下文,用於儲存應用程式中的變數。

g 變數:作為 flask 程式全域性的一個臨時變數,充當者中間媒介的作用,我們可以通過它傳遞一些資料,g 儲存的是當前請求的全域性變數,不同的請求會有不同的全域性變數,通過不同的thread id區別

注意:不同的請求,會有不同的全域性變數

from flask import Flask

# 應用上下文中的變數
from flask import current_app
from flask import g

# 請求上下文中的變數
from flask import session
from flask import request

app = Flask(__name__)


# print(request.method)
# print(session.get['user_id', ''])
# print(current_app.config.get('DEBUG'))

@app.route('/')
def index():
    # print(request.method)
    print(current_app.config.get('DEBUG'))
    return 'index'


if __name__ == '__main__':
    app.run(debug=True)



Flask-Script 擴充套件

通過使用Flask-Script擴充套件,我們可以在Flask伺服器啟動的時候,通過命令列的方式傳入引數。而不僅僅通過app.run()方法中傳參,

pip install flask-script

from flask import Flask
from flask_script import Manager


app = Flask(__name__)
# 建立 manager 與 app 進行關聯
manager = Manager(app)
# 可以通過命令列在執行的時候指定執行的埠


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


if __name__ == '__main__':
    # 需要使用 manager 去執行
    manager.run()



模板

在前面的示例中,檢視函式的主要作用是生成請求的響應,這是最簡單的請求。實際上,檢視函式有兩個作用:處理業務邏輯和返回響應內容。在大型應用中,把業務邏輯和表現內容放在一起,會增加程式碼的複雜度和維護成本。本節學到的模板,它的作用即是承擔檢視函式的另一個作用,即返回響應內容。

  • 模板其實是一個包含響應文字的檔案,其中用佔位符(變數)表示動態部分,告訴模板引擎其具體的值需要從使用的資料中獲取
  • 使用真實值替換變數,再返回最終得到的字串,這個過程稱為“渲染”
  • Flask是使用 Jinja2 這個模板引擎來渲染模板

使用模板的好處:

  • 檢視函式只負責業務邏輯和資料處理(業務邏輯方面)
  • 而模板則取到檢視函式的資料結果進行展示(檢視展示方面)
  • 程式碼結構清晰,耦合度低
jinja2
  • Jinja2:是 Python 下一個被廣泛應用的模板引擎,是由Python實現的模板語言,他的設計思想來源於 Django 的模板引擎,並擴充套件了其語法和一系列強大的功能,其是Flask內建的模板語言。
  • 模板語言:是一種被設計來自動生成文件的簡單文字格式,在模板語言中,一般都會把一些變數傳給模板,替換模板的特定位置上預先定義好的佔位變數名。
  • Flask提供的 render_template 函式封裝了該模板引擎
  • render_template 函式的第一個引數是模板的檔名,後面的引數都是鍵值對,表示模板中變數對應的真實值。

使用

  • {{}} 來表示變數名,這種 {{}} 語法叫做變數程式碼塊
  • 用 {%%} 定義的控制程式碼塊,可以實現一些語言層次的功能,比如迴圈或者if語句
  • 使用 {# #} 進行註釋,註釋的內容不會在html中被渲染出來
from flask import Flask
from flask import render_template

app = Flask(__name__)


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


@app.route('/demo1')
def demo1():
    my_int = 10
    my_str = "<h1>哈哈哈</h1>"
    my_list = [1, 24, 5, 62, 6]
    my_dict = {
        "id": "1",
        "name": "xiaohei"
    }
    return render_template('demo6_template.html',
                           my_int=my_int,
                           my_str=my_str,
                           my_list=my_list,
                           my_dict=my_dict,
                           )

if __name__ == '__main__':
    app.run(debug=True)