1. 程式人生 > >python Flask web框架

python Flask web框架

path pretty spa 記錄 run explain 功能 ng- 錯誤頁面

目錄:

--> Flask
  --> 配置文件
    --> 配置文件解析
    --> 配置文件導入

  --> 路由
    --> 路由參數
    --> 常用路由匹配

  --> 請求相關 & 響應
    --> request 參數
    --> response 參數
    --> 打包模板和參數

  --> 模板 jinja2
    --> 模板導入/繼承
    --> 前段/後端 安全渲染
    --> 前段自定義函數 macro

    --> 後端自定義前段函數
      --> @app.template_global()
      --> @app.template_filter()

  --> 請求擴展
    --> @app.before_first_request
    --> @app.before_request
    --> @app.after_request

  --> 錯誤頁面自定制

  --> 閃現
  --> 藍圖
  --> 中間件
  --> 信號
  --> 上下文管理
    --> threading.local
    --> 請求上下文(requestContext)
      --> request
      --> session
    --> 應用上下文:AppContext
      --> app(current_app)
      --> g
    --> 實現細節
      --> 利用threading.local 的線程 唯一ID標識符
      --> requestcontext對象通過localstack添加到Local 棧中
      --> 導入request(session、current_app、g)是localproxy -> 偏函數 ->Localstack -> Local
      --> requestcontext的auto_pop -> localstaack.pop ->local中移除
    --> 多app應用
    --> 技術點:LocalProxy、chain,偏函數
    --> 中間件/信號 執行順序

falsk 配置文件解析

技術分享圖片
{
    DEBUG:                                get_debug_flag(default=False),  是否開啟Debug模式
    TESTING:                              False,                          是否開啟測試模式
    PROPAGATE_EXCEPTIONS:                 None,                         
    PRESERVE_CONTEXT_ON_EXCEPTION:        None,
    SECRET_KEY:                           None,
    PERMANENT_SESSION_LIFETIME:           timedelta(days=31),
    USE_X_SENDFILE:                       False,
    LOGGER_NAME:                          None,
    LOGGER_HANDLER_POLICY:               always,
    SERVER_NAME:                          None,
    APPLICATION_ROOT:                     None,
    SESSION_COOKIE_NAME:                  session,
    SESSION_COOKIE_DOMAIN:                None,
    SESSION_COOKIE_PATH:                  None,
    SESSION_COOKIE_HTTPONLY:              True,
    SESSION_COOKIE_SECURE:                False,
    SESSION_REFRESH_EACH_REQUEST:         True,
    MAX_CONTENT_LENGTH:                   None,
    SEND_FILE_MAX_AGE_DEFAULT:            timedelta(hours=12),
    TRAP_BAD_REQUEST_ERRORS:              False,
    TRAP_HTTP_EXCEPTIONS:                 False,
    EXPLAIN_TEMPLATE_LOADING:             False,
    PREFERRED_URL_SCHEME:                 http,
    JSON_AS_ASCII:                        True,
    JSON_SORT_KEYS:                       True,
    JSONIFY_PRETTYPRINT_REGULAR:          True,
    JSONIFY_MIMETYPE:                     application/json,
    TEMPLATES_AUTO_RELOAD:                None,
}
View Code

falsk 配置文件導入:

# flask 配置文件介紹:
# 方式一:
# Session, Cookies以及一些第三方擴展都會用到SECRET_KEY值
# app.config[‘SECRET_KEY‘] = ‘123456‘
# app.config[‘DEBUG‘] = True

# 方式二:(只有部分可以如此操作)
# app.secret_key = ‘123456‘

# 方式三:(導入配置文件的方式)
# app.config.from_pyfile(‘setting.py‘)

# 方式四:(推薦方式:在配置文件中寫一個類,將配置寫在類裏面,再導入)
app.config.from_object(setting.Text)

方式四 setting文件

# 共有的
class Config(object):
    SECRET_KEY = 123456
    DEBUG = False

class Text(Config):
    DEBUG = True
    SECRET_KEY = abcd

路由參數

技術分享圖片
# 路由系統:
# 基於裝飾器來實現,但是究其本質是通過下面函數實現的
# app.add_url_rule(rule=‘/路徑‘,endpoint=‘別名‘,view_func=‘視圖函數‘,methods=‘訪問的類型/["GET","POST"]‘)

# CBV 的flask 方式:
# from flask import views
# 配置視圖
# class Text(views.MethodView):
#     methods = [‘GET‘,‘POST‘]
#     # 登錄裝飾器
#     # decorators = [login_required,]
#
#     def get(self):
#         return ‘Text_Class_get‘
#
#     def post(self):
#         return ‘Text_Class,post‘
#
# 配置路由
# app.add_url_rule(rule=‘/text/‘,view_func=Text.as_view(name=‘text‘))

# app.add_url_rule 和 app.route
# 參數:
# rule: url 規則
# view_func 視圖函數名稱
# defaults=None 默認值,當url中沒有參數
# endpoint=None 反向生成url,不能重名,在使用裝飾器時,沒有導入@wraps(func)則需要區分
# methods=None 允許的請求方式,如:[‘GET‘,‘POST‘]
# strict_slashes=None 對URL最後的 / 符號是否嚴格要求
# redirect_to=None 重定向地址
# subdomain=None 子域名訪問,需要配合域名使用
View Code

常用路由匹配方式

技術分享圖片
# 常用路由方式:
# @app.route(‘/user/<username>‘)
# @app.route(‘/user/<int:post_id>‘)
# @app.route(‘/user/<float:post_id>‘)
# @app.route(‘/user/<path:PATH>‘)
# @app.route(‘/user/‘,methods=[‘GET‘,‘POST‘])
View Code

請求相關 & 響應

request

技術分享圖片
# request
# request.method
# request.args
# request.form
# request.values
# request.cookies
# request.headers
# request.path
# request.full_path
# request.script_root
# request.url
# request.base_url
# request.url_root
# request.host_url
# request.host

# request.files
# obj = request=files[‘the_file_name‘]
# obj.save(‘/var/www/uploads‘+secure_filename(f.filename))
View Code

response

技術分享圖片
# response
# return "字符串"
# render_templates(‘html模板‘,返回參數)
# return redirect(‘/index.html‘)

# 打包模板和參數
# response = make_response(render_template("index.html"))
# response 是flask.wrappers.Response類型
# response.delete_cookie("key")
# response.set_cookie("key",‘value‘)
# response.headers[‘X-something‘] = ‘A value‘
# return response
View Code

模板 jinja2

技術分享圖片
# 模板:(模板也支持導入,繼承)
# 跟 django 都是使用的jinja2 模板
# 前段 安全渲染 {func | safe }
# 後端 安全渲染  import Markup 函數

# # 新增功能
# {% macro 自定義函數名(參數,參數N) %}
#     <span>{{參數}}---{{參數n}}</span>
#     <span>{{參數}}---{{參數n}}</span>
#     <span>{{參數}}---{{參數n}}</span>
# {% endmacro%}
#
# # 調用上面的函數
# {{自定義函數(參數)}}

# ------------

# 模板內自定義函數(類似django simple_tag)
# @app.template_global()
# def sb(a1,a2):
#     return a1 + a2

# 模板中使用方法:
# {{sb(1,2)}}


# ------------
# 後端自定義前段函數
# @app.template_filter()
# def sb2(a1,a2,a3):
#     return a1 + a2 + a3

# 模板中使用方法:
# {{1|sb2(2,3)}}
View Code

請求擴展

技術分享圖片
# 請求擴展 (django中的中間件)

# 第一次訪問執行:
# @app.before_first_request
# def first(*args,**kwargs)
#     pass

# # 先進後出
# @app.before_request
# def preocess_request(*args,**kwargs):
#     # 這裏可以做登錄認證
#     print(‘request_1‘)
#
# @app.before_request
# def preocess_request2(*args,**kwargs):
#     # 這裏可以做登錄認證
#     print(‘request_2‘)
#
# @app.after_request
# def preocess_response(response):
#     print(‘response_1‘)
#     return response
#
# @app.after_request
# def preocess_response2(response):
#     print(‘response_2‘)
#     return response
View Code

錯誤頁面自定制

# # 錯誤頁面 自定制
#
# @app.errorhandler(404)
# def error_404(arg):
#     return "頁面不存在!"

閃現

# # 閃現 : 臨時數據操作 如:顯示錯誤信息
# from flask import flash,get_flashed_messages
#
# @app.route(‘/get‘)
# def get():
#     data = get_flashed_messages()
#     # data = get_flashed_messages(category_filter=[‘l1‘]) 分類取數據
#     print(data)
#     return "get"
#
# @app.route(‘/set‘)
# def set():
#     flash(‘aaaaaa‘)
#     # flash(‘aaaaaa‘,category=‘l1‘) #可以數據分類
#     return "set"

上下文管理

技術分享圖片
# 上下文管理:
# 1.知道flask 依賴的組件: WSGI、jinja2、Werkzeug,flask 框架是組裝件的粘合劑
# client --- wsgi -----flask --- jinja2 ----werkzeug
# 2.flask 用threading.local 做了一個Local對象 存放請求信息

# 引入步驟:
# Flask()實例化 -> 將請求數據傳入Flask實例 -> 將數據push 至 _request_ctx_stack,_request_ctx_stack實質是 LocalStack()
# LocalStack() 初始化時 又實例化了 Local() ,local初始化 最終以類似列表的方式 保存object.__setattr__(self, ‘__storage__‘, {})

# 請求到來:
#   --ctx = 封裝requestContext(request,ssision)
#   --ctx放進Local中
# 執行視圖時
#   --導入request
#   --request   ---> LocalProxy對象中 __getattr__獲取對應的請求
#       ---調用 _lookup_req_object函數:去local 中醬requestcontext 將獲取到,再去requestcontext中獲取request或session
# 請求結束
#   --ctx.auto_pop()
#   --ctx從local中移除

# --> 上下文管理
#       --> threading.local
# --> 請求上下文(requestContext)
#       --> request
#       --> session
# --> 應用上下文: AppContext
#       --> app(current_app)
#       --> g
# --> 實現細節
#       --> 利用threading.local的線程唯一ID標識符
#       --> requestcontext對象通過localstack添加到Local棧中
#       --> 導入request(session、current_app、g)是localproxy -> 偏函數 ->Localstack -> Local
#       --> requestcontext的auto_pop -> localstaack.pop ->local中移除
View Code

信號

技術分享圖片
# # 示例一:(自定義信號)
# from flask.signals import _signals
#
# xinhao = _signals.signal(‘before-render-template‘)#創建信號
#
# #定義函數
# def wahaha(*args,**kwargs):
#     print("111",args,kwargs)
#
# # 將函數註冊到信號中,添加到這個列表
# xinhao.connect(wahaha)
#
# @app.route("/zzz")
# def zzz():
#     # 信號是通過send 方法 出發的!!!!!!!!!
#     xinhao.send(sender=‘xxx‘,a1=123,a2=456)  #觸發這個信號,執行註冊到這個信號列表中的所有函數,此處的參數個數需要與定義的函數中的參數一致
#     print("ok")
#     return "OK"

# ---------------------------------------------------------------

# # 示例二:(內置信號使用)
# from flask.signals import _signals
#
# bf = _signals.signal(‘before-render-template‘)
# bf2 = _signals.signal(‘template-rendered‘)
#
# def text(*args,**kwargs):
#     print("111")
#
# bf.connect(text)
# bf2.connect(text)
View Code

信號與中間件的執行順序

技術分享圖片
# 信號 依賴於 blinker 模塊 (信號只執行記錄,並不能中斷流程,而中間件可以中斷)
# pip install blinker

# 內置信號 以及執行順序:
# --> a 中間件:before_first_request
# --->1 appcontext_pushed = _signals.signal(‘appcontext-pushed‘)  # 請求app上下文push時執行
# --->2 request_started = _signals.signal(‘request-started‘)  # 請求到來前執行

# --> b 中間件:before_request
# --->3 before_render_template = _signals.signal(‘before-render-template‘)  # 模板渲染前執行
# --->4.template_rendered = _signals.signal(‘template-rendered‘)  # 模板渲染後執行

# --> c 中間件:after_request
# -->   session.save_session()
# --->5 request_finished = _signals.signal(‘request-finished‘)  # 請求結束後執行
# --->6.request_tearing_down = _signals.signal(‘request-tearing-down‘)  # 請求執行完畢後自動執行(無論成功與否)

# --->7.appcontext_tearing_down = _signals.signal(‘appcontext-tearing-down‘)  # 請求上下文執行完畢後自動執行(無論成功與否)
# --->8.appcontext_popped = _signals.signal(‘appcontext-popped‘)  # 請求上下文pop時執行

# 發生在2 / 3 / 4 / 5或不執行
# got_request_exception = _signals.signal(‘got-request-exception‘)  # 請求執行出現異常時執行

# message_flashed = _signals.signal(‘message-flashed‘)  # 調用flask在其中添加數據時,自動觸發
View Code

多APP 應用

技術分享圖片
# 多APP 應用
# web 訪問多app應用時,上下文管理是如何實現的?

# from werkzeug.wsgi import DispatcherMiddleware
# from werkzeug.serving import run_simple
#
# app01 = Flask(‘app01‘)
# app02 = Flask(‘app02‘)
#
# @app01.route(‘/index‘)
# def app01_index():
#     return "app01"
#
# @app02.route(‘/index‘)
# def app02_index():
#     return "app02"
#
# app = DispatcherMiddleware(app01,{
#     ‘/app02前綴‘:app02,
# })
#
# if __name__=="__main__":
#     run_simple(‘localhost‘,8002,app)
View Code

session 相關

#&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 使用session 前 必須配置SECRET_KEY!!!
# session 操作:(session就相當於是一個字典!)
# session[‘類型‘] = 值
# session.pop[‘類型‘]
# del session[‘類型‘]
#&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

session 第三方組件(自定制session保存位置)

技術分享圖片
# flask session 機制
‘‘‘
->請求剛進來:獲取隨機字符串,存在,則去容器 中獲取原來的個人數據,否則創建一個空的容器
    ->對象(隨機字符串,{請求數據})
->視圖:讀取內存中對象
->響應:內存對象 將對象數據保存到數據庫(沒有指定數據庫的話 保存在內存中),把隨機字符串寫到用戶cookie中
‘‘‘
# 使用 方式一
# flask-session 組件
# from flask_session import Session
# import pymongo
#
# app.config[‘SESSION_TYPE‘] = ‘mongodb‘  # session類型為redis
#
# app.config[‘SESSION_MONGODB‘] = pymongo.MongoClient(‘localhost‘,27017)
# app.config[‘SESSION_MONGODB_DB‘] = ‘text‘
# app.config[‘SESSION_MONGODB_COLLECT‘] = ‘col‘
#
# app.config[‘SESSION_PERMANENT‘] = True  # 如果設置為True,則關閉瀏覽器session就失效。
# app.config[‘SESSION_USE_SIGNER‘] = False  # 是否對發送到瀏覽器上session的cookie值進行加密
# app.config[‘SESSION_KEY_PREFIX‘] = ‘session:‘  # 保存到session中的值的前綴
#
# Session(app)


# 使用 方式二

# from flask_session import MongoDBSessionInterface
# app.session_interface = MongoDBSessionInterface("參數!")
View Code

一些問答

# &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
# 問題1 多線程時如何體現的?
# 問題2 flask的local 中保存數據時,使用列表創建出來的棧,為什麽用棧?
#       --如果寫web程序,web運行環境,棧中永遠保存1條數據(可以不用棧)
#       --寫腳本獲取app信息,可能存在app上下文嵌套關系

‘‘‘
from  flask import Flask,current_app,globals,_app_ctx_stack

app1=Flask(‘app01‘)
app1.debug = False

app2=Flask(‘app02‘)
app2.debug = False

with app1.app_context():
    print(_app_ctx_stack._local.__storage__)
    print(current_app.config[‘DEBUG‘])

    with app2.app_context():
    print(_app_ctx_stack._local.__storage__)
    print(current_app.config[‘DEBUG‘])

‘‘‘
# &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&

flask 實現裝飾器方式:引入(wraps)

技術分享圖片
from functools import wraps
# flask 實現登錄裝飾器的功能
def login_required(func):
    @wraps(func)
    # @wraps(view_func)的作用:     不改變使用裝飾器原有函數的結構(如__name__, __doc__)
    # 不使用wraps可能出現的ERROR:   view_func...endpoint...map...
    def wrapper(*args,**kwargs):
        if session.get(user):
            return func(*args,**kwargs)
        else:
            return redirect(/)
    return wrapper

# 裝飾器實現的方式:
@app.route(/index/,methods=[GET])
@login_required                                         # flask 登錄裝飾器
def index():
    return render_template(index.html,u_list=data)

@app.route("/detail/<int:id>",methods=[GET])
def detail(id):
    return str(id)
View Code

python Flask web框架