python之flask框架詳解

目錄

前言

1、新建文件helloworld.py

2、相關配置參數

3.加載配置文件

3.1配置對象加載

3.2配置文件加載

3.3讀取配置

4 路由定義

4.1指定路由地址

4.2 給路由傳參

4.3指定請求方式

4.4 正則匹配路由

5.簡單視圖

5.1 返回json

5.2重定向

5.3自定義狀態碼

6、異常捕獲

6.1 HTTP主動拋出異常

6.2捕獲錯誤

 7、勾子函數

 8. request請求參數

9、flask上下文參數

10.cookie使用

11、Session使用

12、藍圖Blueprint

運行機制

藍圖的url前綴

註冊靜態路由

設置模版目錄

13、Flask-Script 擴展


前言

Flask 本身相當於一個內核,其他幾乎所有的功能都要用到擴展,都需要用第三方的擴展來實現,比如可以用 Flask 擴展加入ORM、窗體驗證工具,文件上傳、身份驗證等。Flask 沒有默認使用的數據庫,你可以選擇 MySQL,也可以用 NoSQL。

其 WSGI 工具箱採用 Werkzeug(路由模塊),模板引擎則使用 Jinja2。這兩個也是 Flask 框架的核心。

Flask常用擴展包:

  • Flask-SQLalchemy:操作數據庫;
  • Flask-script:插入腳本;
  • Flask-migrate:管理遷移數據庫;
  • Flask-Session:Session存儲方式指定;
  • Flask-WTF:表單;
  • Flask-Mail:郵件;
  • Flask-Bable:提供國際化和本地化支持,翻譯;
  • Flask-Login:認證用戶狀態;
  • Flask-OpenID:認證;
  • Flask-RESTful:開發REST API的工具;
  • Flask-Bootstrap:集成前端Twitter Bootstrap框架;
  • Flask-Moment:本地化日期和時間;
  • Flask-Admin:簡單而可擴展的管理接口的框架
  1. 中文文檔(http://docs.jinkan.org/docs/flask/
  2. 英文文檔(http://flask.pocoo.org/docs/0.11/
  3. 擴展列表:http://flask.pocoo.org/extensions/
#安裝
pip install flask==0.10.1      # 後面爲版本號

1、新建文件helloworld.py

# 導入Flask類
from flask import Flask
# Flask函數接收一個參數__name__,它會指向程序所在的包

app = Flask(__name__)

# 裝飾器的作用是將路由映射到視圖函數 index
@app.route('/')
def index():
    return 'Hello World'

# Flask應用程序實例的 run 方法 啓動 WEB 服務器

if __name__ == '__main__':
    app.run()    # 可以指定運行的主機IP地址,端口,是否開啓調試模式
    # app.run(host="0.0.0.0", port=8888, debug = True)

 

2、相關配置參數

Flask 程序實例在創建的時候,需要默認傳入當前 Flask 程序所指定的包(模塊)

app = Flask(__name__)

  • import_name
    • Flask程序所在的包(模塊),傳 __name__ 就可以
    • 其可以決定 Flask 在訪問靜態文件時查找的路徑
  • static_path
    • 靜態文件訪問路徑(不推薦使用,使用 static_url_path 代替)
  • static_url_path
    • 靜態文件訪問路徑,可以不傳,默認爲:/ + static_folder
  • static_folder
    • 靜態文件存儲的文件夾,可以不傳,默認爲 static
  • template_folder
    • 模板文件存儲的文件夾,可以不傳,默認爲 templates

3.加載配置文件

在 Flask 程序運行的時候,可以給 Flask 設置相關配置,比如:配置 Debug 模式,配置數據庫連接地址等等,設置 Flask 配置有以下三種方式:

  • 從配置對象中加載(常用)
    • app.config.form_object()
  • 從配置文件中加載
    • app.config.form_pyfile()
    從環境變量中加載(不常用)
    • app.config.from_envvar()

3.1配置對象加載

# 配置對象,裏面定義需要給 APP 添加的一系列配置
class Config(object):
    DEBUG = True


# 創建 Flask 類的對象,指向程序所在的包的名稱
app = Flask(__name__)

# 從配置對象中加載配置
app.config.from_object(Config)

 

3.2配置文件加載

創建配置文件 config.ini,在配置文件中添加配置

格式eg: DEBUG = True

# 創建 Flask 類的對象,指向程序所在的包的名稱
app = Flask(__name__)

# 從配置文件中加載配置
app.config.from_pyfile('config.ini')

3.3讀取配置

  • app.config.get()
  • 在視圖函數中使用 current_app.config.get()

注:Flask 應用程序將一些常用的配置設置成了應用程序對象的屬性,也可以通過屬性直接設置/獲取某些配置:app.debug = True

4 路由定義

4.1指定路由地址

# 指定訪問路徑爲 hello
@app.route('/hello')
def demo1():
    return 'hello world'

4.2 給路由傳參

# 路由傳遞參數
@app.route('/user/<user_id>')
def user_info(user_id):
    return 'hello %s' % user_id

# 路由傳遞的參數默認當做 string 處理,也可以指定參數的類型

# 路由傳遞參數
@app.route('/user/<int:user_id>')
def user_info(user_id):
    return 'hello %d' % user_id

 

4.3指定請求方式

在 Flask 中,定義一個路由,默認的請求方式爲:

  • GET
  • OPTIONS(自帶)
  • HEAD(自帶)
@app.route('/login', methods=['GET', 'POST'])  # 支持GET和POST,並且支持自帶的OPTIONS和HEAD
def login():
    # 直接從請求中取到請求方式並返回
    return request.method

4.4 正則匹配路由

在 web 開發中,可能會出現限制用戶訪問規則的場景,那麼這個時候就需要用到正則匹配,根據自己的規則去限定請求參數再進行訪問

具體實現步驟爲:

  • 導入轉換器基類:在 Flask 中,所有的路由的匹配規則都是使用轉換器對象進行記錄
  • 自定義轉換器:自定義類繼承於轉換器基類
  • 添加轉換器到默認的轉換器字典中
  • 使用自定義轉換器實現自定義匹配規則

(1)導入轉換器基類

from werkzeug.routing import BaseConverter

(2)自定義轉換器

from flask import Flask, redirect, url_for
from werkzeug.routing import BaseConverter


class RegexConverter(BaseConverter):
    def __init__(self, url_name, *args):
        super().__init__(url_name)
        # 將接受的第1個參數當作匹配規則進行保存
        self.regex = args[0]


class ListConverter(BaseConverter):
    regex = '(\\d+,?)+\\d$'

    def to_python(self, value):
        return value.split(",")

    def to_url(self, value):
        return ",".join(str(i) for i in value)

 

(3)添加轉換器到默認的轉換器字典中,並指定轉換器使用時名字爲: re

app = Flask(__name__)
# 將自定義轉換器添加到轉換器字典中,並指定轉換器使用時名字爲: re
app.url_map.converters["re"] = RegexConverter
# 將自定義轉換器添加到轉換器字典中,並指定轉換器使用時名字爲: list
app.url_map.converters["list"] = ListConverter

(4)使用轉換器去實現自定義匹配規則

@app.route("/demo/<re('\d{6}'):use_name>")
def demo1(use_name):
    return "用戶名是 %s" % use_name


@app.route("/users/<list:user_list>")
def demo11(user_list):
    return "用戶列表爲 %s" % user_list

 

 

# 系統自帶轉換器
DEFAULT_CONVERTERS = {
    'default':          UnicodeConverter,
    'string':           UnicodeConverter,
    'any':              AnyConverter,
    'path':             PathConverter,
    'int':              IntegerConverter,
    'float':            FloatConverter,
    'uuid':             UUIDConverter,
}
# 系統自帶的轉換器具體使用方式在每種轉換器的註釋代碼中有寫,請留意每種轉換器初始化的參數

 

自定義轉換器其他兩個函數實現:

繼承於自定義轉換器之後,還可以實現 to_python 和 to_url 這兩個函數去對匹配參數做進一步處理:

  • to_python:
    • 該函數參數中的 value 值代表匹配到的值,可輸出進行查看
    • 匹配完成之後,對匹配到的參數作最後一步處理再返回,比如:轉成 int 類型的值再返回:
class RegexConverter(BaseConverter):
    def __init__(self, url_map, *args):
        super(RegexConverter, self).__init__(url_map)
        # 將接受的第1個參數當作匹配規則進行保存
        self.regex = args[0]

    def to_python(self, value):
        return int(value)   # 在視圖函數中可以查看參數的類型,由之前默認的 str 已變成 int 類型的值

 

  • to_url:
    • 在使用 url_for 去獲取視圖函數所對應的 url 的時候,會調用此方法對 url_for 後面傳入的視圖函數參數做進一步處理
    • 具體可參見 Flask 的 app.py 中寫的示例代碼:ListConverter

5.簡單視圖

5.1 返回json

在使用 Flask 給客戶端返回 JSON 數據時,可以直接使用 jsonify 生成一個 JSON 的響應

# 返回JSON
@app.route('/demo')
def demo():
    json_dict = {
        "user_id": 10,
        "user_name": "laowang"
    }
    return jsonify(json_dict)
注:不推薦使用 json.dumps 轉成 JSON 字符串直接返回,因爲返回的數據要符合 HTTP 協議規範,如果是 JSON 需要指定 content-type:application/json

 

5.2重定向

(1)重定向到百度

# 重定向
@app.route('/demo')
def demo():
    return redirect('http://www.baidu.com')

(2)重定向到自己寫的視圖函數

  • 直接填寫自己 url 路徑
  • 使用 url_for 生成指定視圖函數所對應的 url
@app.route('/demo1')
def demo1():
    return 'demo1'

# 重定向,採用url_for生成demo1對應的url
@app.route('/demo2')
def demo2():
    return redirect(url_for('demo1'))

 

(3)重定向到帶有參數的視圖函數

# 路由傳遞參數
@app.route('/user/<int:user_id>')
def user_info(user_id):
    return 'hello %d' % user_id

# 重定向,在url_for中傳入參數
@app.route('/demo5')
def demo5():
    # 使用 url_for 生成指定視圖函數所對應的 url
    return redirect(url_for('user_info', user_id=100))

 

5.3自定義狀態碼

自定義不符合 http 協議的狀態碼

@app.route('/demo')
def demo():
    return '狀態碼爲 666', 666

6、異常捕獲

6.1 HTTP主動拋出異常

  • abort 方法
    • 拋出一個給定狀態代碼的 HTTPException 或者 指定響應,例如想要用一個頁面未找到異常來終止請求,你可以調用 abort(404)。
  • 參數:
    • code – HTTP的錯誤狀態碼
# abort(404)
# 拋出狀態碼的話,只能拋出 HTTP 協議的錯誤狀態碼
abort(500)

6.2捕獲錯誤

  • errorhandler 裝飾器
    • 註冊一個錯誤處理程序,當程序拋出指定錯誤狀態碼的時候,就會調用該裝飾器所裝飾的方法
  • 參數:
    • code_or_exception – HTTP的錯誤狀態碼或指定異常

例如統一處理狀態碼爲500的錯誤給用戶友好的提示:

# 處理所有500類型的異常

@app.errorhandler(500)
def internal_server_error(e):
    return '服務器搬家了'


# 處理特定的異常項
@app.errorhandler(ZeroDivisionError)
def zero_division_error(e):
    return '除數不能爲0'

 

 7、勾子函數

在客戶端和服務器交互的過程中,有些準備工作或掃尾工作需要處理,比如:

  • 在請求開始時,建立數據庫連接;
  • 在請求開始時,根據需求進行權限校驗;
  • 在請求結束時,指定數據的交互格式;

爲了讓每個視圖函數避免編寫重複功能的代碼,Flask提供了通用設施的功能,即請求鉤子。

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

  • before_first_request
    • 在處理第一個請求前執行
  • before_request
    • 在每次請求前執行
    • 如果在某修飾的函數中返回了一個響應,視圖函數將不再被調用
  • after_request
    • 如果沒有拋出錯誤,在每次請求後執行
    • 接受一個參數:視圖函數作出的響應
    • 在此函數中可以對響應值在返回之前做最後一步修改處理
    • 需要將參數中的響應在此參數中進行返回
  • teardown_request:
    • 在每次請求後執行
    • 接受一個參數:錯誤信息,如果有相關錯誤拋出
from flask import Flask
from flask import abort

app = Flask(__name__)


# 在第一次請求之前調用,可以在此方法內部做一些初始化操作
@app.before_first_request
def before_first_request():
    print("before_first_request")


# 在每一次請求之前調用,這時候已經有請求了,可能在這個方法裏面做請求的校驗
# 如果請求的校驗不成功,可以直接在此方法中進行響應,直接return之後那麼就不會執行視圖函數
@app.before_request
def before_request():
    print("before_request")
    # if 請求不符合條件:
    #     return "laowang"


# 在執行完視圖函數之後會調用,並且會把視圖函數所生成的響應傳入,可以在此方法中對響應做最後一步統一的處理
@app.after_request
def after_request(response):
    print("after_request")
    response.headers["Content-Type"] = "application/json"
    return response


# 請每一次請求之後都會調用,會接受一個參數,參數是服務器出現的錯誤信息
@app.teardown_request
def teardown_request(e):
    print("teardown_request")


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

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

 

執行結果如下:

在第1次請求時的打印:
before_first_request
before_request
after_request
teardown_request

在第2次請求時的打印:
before_request
after_request
teardown_request

 

 8. request請求參數

  request 就是flask中代表當前請求的 request 對象,作爲請求上下文變量(理解成全局變量,在視圖函數中直接使用可以取到當前本次請求)

  常用屬性:

屬性 說明 類型
data 記錄請求的數據,並轉換爲字符串 *
form 記錄請求中的表單數據 MultiDict
args 記錄請求中的查詢參數 MultiDict
cookies 記錄請求中的cookie信息 Dict
headers 記錄請求中的報文頭 EnvironHeaders
method 記錄請求使用的HTTP方法 GET/POST
url 記錄請求的URL地址 string
files 記錄請求上傳的文件 *

 

 

 

 

 

 

 

 

# 獲取上傳的圖片並保存到本地
@app.route('/', methods=['POST'])
def index():
    pic = request.files.get('pic')
    pic.save('./static/aaa.png')
    return 'index'

 

9、flask上下文參數

  上下文:相當於一個容器,保存了 Flask 程序運行過程中的一些信息。

  Flask中有兩種上下文,請求上下文(request context)和應用上下文(application context)

  1. application 指的就是當你調用app = Flask(__name__)創建的這個對象app

  2. request 指的是每次http請求發生時,WSGI server(比如gunicorn)調用Flask.__call__()之後,在Flask對象內部創建的Request對象;

  3. application 表示用於響應WSGI請求的應用本身,request 表示每次http請求;

  4. application的生命週期大於request,一個application存活期間,可能發生多次http請求,所以,也就會有多個request

  源碼瞭解一下 flask 如何實現這兩種context: 

# 代碼摘選自flask 0.5 中的ctx.py文件, 進行了部分刪減
class _RequestContext(object):
    
    def __init__(self, app, environ):
        self.app = app
        self.request = app.request_class(environ)
        self.session = app.open_session(self.request)
        self.g = _RequestGlobals()

# flask 使用_RequestContext的代碼如下:
class Flask(object):

    def request_context(self, environ):
        return _RequestContext(self, environ)

 

Flask類中,每次請求都會調用這個request_context函數。這個函數則會創建一個_RequestContext對象。這個對象在創建時,將Flask實例的本身作爲實參傳入_RequestContext自身,因此,self.app = Flask()

所以,雖然每次http請求都會創建一個_RequestContext對象,但是,每次創建的時候都會將同一個Flask對象傳入該對象的app成員變量,使得:

由同一個Flask對象響應的請求所創建的_RequestContext對象的app成員變量都共享同一個application

通過在Flask對象中創建_RequestContext對象,並將Flask自身作爲參數傳入_RequestContext對象的方式,實現了多個request context對應一個application context 的目的。  

(1)請求上下文(request context)

  請求上下文對象有:request、session

 

  • request

封裝了HTTP請求的內容,針對的是http請求。舉例:user = request.args.get('user'),獲取的是get請求的參數。

  • session

用來記錄請求會話中的信息,針對的是用戶信息。舉例:session['name'] = user.id,可以記錄用戶信息。還可以通過session.get('name')獲取用戶信息。

(2)應用上下文(application context)

  應用上下文對象有:current_app,g

  • current_app

  應用程序上下文,用於存儲應用程序中的變量,可以通過current_app.name打印當前app的名稱,也可以在current_app中存儲一些變量,例如:

  1. 應用的啓動腳本是哪個文件,啓動時指定了哪些參數
  2. 加載了哪些配置文件,導入了哪些配置
  3. 連了哪個數據庫
  4. 有哪些public的工具類、常量
  5. 應用跑再哪個機器上,IP多少,內存多大
current_app.name
current_app.test_value='value'
  • g變量

  g 作爲 flask 程序全局的一個臨時變量,充當中間媒介的作用,我們可以通過它傳遞一些數據,g 保存的是當前請求的全局變量,不同的請求會有不同的全局變量,通過不同的thread id區別 

g.name='zhangsan'
  1. 請求上下文:保存了客戶端和服務器交互的數據
  2. 應用上下文:flask 應用程序運行過程中,保存的一些配置信息,比如程序名、數據庫連接、應用信息等

10.cookie使用

Cookie:指某些網站爲了辨別用戶身份、進行會話跟蹤而儲存在用戶本地的數據(通常經過加密)。

  • 複數形式Cookies。
  • Cookie是由服務器端生成,發送給客戶端瀏覽器,瀏覽器會將Cookie的key/value保存,下次請求同一網站時就發送該Cookie給服務器(前提是瀏覽器設置爲啓用cookie)。
  • Cookie的key/value可以由服務器端自己定義。
# 設置cookie
# 當瀏覽器請求某網站時,會將本網站下所有Cookie信息提交給服務器,所以在request中可以讀取Cookie信息

from flask imoprt Flask, make_response
@app.route('/cookie')
def set_cookie():
    resp = make_response('this is to set cookie')
    resp.set_cookie('username', 'zhangsan')
    resp.set_cookie("pwd", "12321")     
    return resp

# 設置過期時間
@app.route('/cookie')
def set_cookie():
    response = make_response('hello world')
    response.set_cookie('username', 'zhangsan', max_age=3600)  # 過期時間爲3600秒
    return response


# 獲取cookie
from flask import Flask, request

@app.route('/request')
def resp_cookie():
    resp = request.cookies.get('username')
    return resp

# 刪除cookie
@app.route("/logout")
def logout():
    resp = make_response("success")
    resp.delete_cookie("username")
    resp.delete_cookie("pwd")
    return resp

 

11、Session使用

# session數據的獲取
# session:請求上下文對象,用於處理http請求中的一些數據內容

# 設置secret_key
# 作用:設置一個secret_key值,用作各種 HASH
app.secret_key = 'python'
# 考慮到安全性, 這個密鑰不建議存儲在程序中. 最好的方法是存儲在你的系統環境變量中, 通過 os.getenv(key, default=None) 獲得.
    

#設置session,並重定向到獲取session的index函數
@app.route("/login")
def login():
    session["username"] = 'zhangsan'
    session['password'] = "1234321"
    return redirect(url_for("index"))


# 獲取session
@app.route("/")
def index():
    username = session.get("username")
    password = session.get("password")
    return "世界真美好%s======%s" % (username, password)


# 刪除session,並重定向到獲取session的index函數
@app.route("/logout")
def logout():
    session.pop("username")
    session.pop("password")
    return redirect(url_for("index"))

 

12、藍圖Blueprint

Blueprint 是一個存儲操作方法的容器,這些操作在這個Blueprint 被註冊到一個應用之後就可以被調用,Flask 可以通過Blueprint來組織URL以及處理請求。

Flask使用Blueprint讓應用實現模塊化,在Flask中,Blueprint具有如下屬性:

  • 一個應用可以具有多個Blueprint
  • 可以將一個Blueprint註冊到任何一個未使用的URL下比如 “/”、“/sample”或者子域名
  • 在一個應用中,一個模塊可以註冊多次
  • Blueprint可以單獨具有自己的模板、靜態文件或者其它的通用操作方法,它並不是必須要實現應用的視圖和函數的
  • 在一個應用初始化時,就應該要註冊需要使用的Blueprint

但是一個Blueprint並不是一個完整的應用,它不能獨立於應用運行,而必須要註冊到某一個應用中。

藍圖/Blueprint對象用起來和一個應用/Flask對象差不多,最大的區別在於一個 藍圖對象沒有辦法獨立運行,必須將它註冊到一個應用對象上才能生效

使用藍圖可以分爲三個步驟

  • 1,創建一個藍圖對象
admin=Blueprint('admin',__name__)
  • 2,在這個藍圖對象上進行操作,註冊路由,指定靜態文件夾,註冊模版過濾器
@admin.route('/admin')
def index():
    return 'admin_home'
  • 3,在應用對象上註冊這個藍圖對象
app.register_blueprint(admin,url
_prefix='/admin')

當這個應用啓動後,通過/admin/admin/可以訪問到藍圖中定義的視圖函數

運行機制

  • 藍圖是保存了一組將來可以在應用對象上執行的操作,註冊路由就是一種操作
  • 當在應用對象上調用 route 裝飾器註冊路由時,這個操作將修改對象的url_map路由表
  • 然而,藍圖對象根本沒有路由表,當我們在藍圖對象上調用route裝飾器註冊路由時,它只是在內部的一個延遲操作記錄列表defered_functions中添加了一個項
  • 當執行應用對象的 register_blueprint() 方法時,應用對象將從藍圖對象的 defered_functions 列表中取出每一項,並以自身作爲參數執行該匿名函數,即調用應用對象的 add_url_rule() 方法,這將真正的修改應用對象的路由表

藍圖的url前綴

  • 當我們在應用對象上註冊一個藍圖時,可以指定一個url_prefix關鍵字參數(這個參數默認是/)
  • 在應用最終的路由表 url_map中,在藍圖上註冊的路由URL自動被加上了這個前綴,這個可以保證在多個藍圖中使用相同的URL規則而不會最終引起衝突,只要在註冊藍圖時將不同的藍圖掛接到不同的自路徑即可

  • url_for

url_for('admin.index') # /admin/admin/   將admin下index函數路由註冊   

註冊靜態路由

和應用對象不同,藍圖對象創建時不會默認註冊靜態目錄的路由。需要我們在 創建時指定 static_folder 參數。

下面的示例將藍圖所在目錄下的static_admin目錄設置爲靜態目錄

admin = Blueprint("admin",__name__,static_folder='static_admin')
app.register_blueprint(admin,url_prefix='/admin')

現在就可以使用/admin/static_admin/ 訪問static_admin目錄下的靜態文件了 定製靜態目錄URL規則 :可以在創建藍圖對象時使用 static_url_path 來改變靜態目錄的路由。下面的示例將爲 static_admin 文件夾的路由設置爲 /lib

admin = Blueprint("admin",__name__,static_folder='static_admin',static_url_path='/lib')
app.register_blueprint(admin,url_prefix='/admin')

設置模版目錄

藍圖對象默認的模板目錄爲系統的模版目錄,可以在創建藍圖對象時使用 template_folder 關鍵字參數設置模板目錄

admin = Blueprint('admin',__name__,template_folder='my_templates')

注:如果在 templates 中存在和 my_templates 同名文件,則系統會優先使用 templates 中的文件,參考鏈接:https://stackoverflow.com/questions/7974771/flask-blueprint-template-folder

13、Flask-Script 擴展

通過使用Flask-Script擴展,我們可以在Flask服務器啓動的時候,通過命令行的方式傳入參數。而不僅僅通過app.run()方法中傳參,比如我們可以通過:

python main.py runserver -host ip地址 -P 端口

 

# 1 、安裝 Flask-Script 擴展
pip install flask-script


# 2、集成 Flask-Script
from flask import Flask
from flask_script import Manager

app = Flask(__name__)
# 把 Manager 類和應用程序實例進行關聯
manager = Manager(app)

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

if __name__ == "__main__":
    manager.run()

 

 

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