Flask中current_app和g對象

 

Flask零基礎到項目實戰(七)請求方法、g對象和鉤子函數

1、get方法

2、post方法

post請求在模板中要注意幾點:html

  • input標籤中,要寫name來標識這個value的key,方便後臺獲取。
  • 在寫form表單的時候,要指定method='post',而且要指定action='/login/'
  • 示例代碼:
<form action="{{ url_for('login') }}" method="post">
            <table>
                <tbody>
                    <tr>
                        <td>用戶名:</td>
                        <td><input type="text" placeholder="請輸入用戶名" name="username"></td>
                    </tr>
                    <tr>
                        <td>密碼:</td>
                        <td><input type="text" placeholder="請輸入密碼" name="password"></td>
                    </tr>
                    <tr>
                        <td></td>
                        <td><input type="submit" value="登陸"></td>
                    </tr>
                </tbody>
            </table>
        </form>

3、g對象

g:global
1. g對象是專門用來保存用戶的數據的。
2. g對象在一次請求中的全部的代碼的地方,都是可使用的。web

使用步驟:
1.建立一個utils.py文件,用於測試除主文件之外的g對象的使用
utils.py數據庫

#encoding: utf-8

from flask import g def login_log(): print u'當前登陸用戶是:%s' % g.username def login_ip(): print u'當前登陸用戶的IP是:%s' % g.ip

2.在主文件中調用utils.py中的函數flask

#encoding: utf-8

from flask import Flask,g,request,render_template from utils import login_log,login_ip app = Flask(__name__) @app.route('/') def hello_world(): return 'Hello World!' @app.route('/login/',methods=['GET', 'POST']) def login(): if request.method == 'GET': return render_template('login.html') else: username = request.form.get('username') password = request.form.get('password') g.username = username g.ip = password login_log() login_ip() return u'恭喜登陸成功!'

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

測試:
 如今在瀏覽器裏面,訪問兩次服務器,效果以下:瀏覽器

 

 

 

4、鉤子函數
 在程序正常運行的時候,程序按照A函數—->B函數的順序依次運行;鉤子函數能夠插入到A函數到B函數運行中間從而,程序運行順序變成了A—->鉤子函數—->B函數。服務器

 Flask項目中有兩個上下文,一個是應用上下文(app),另一個是請求上下文(request)。請求上下文request和應用上下文current_app都是一個全局變量。全部請求都共享的。Flask有特殊的機制能夠保證每次請求的數據都是隔離的,即A請求所產生的數據不會影響到B請求。因此能夠直接導入request對象,也不會被一些髒數據影響了,而且不須要在每一個函數中使用request的時候傳入request對象。這兩個上下文具體的實現方式和原理能夠不必詳細瞭解。只要瞭解這兩個上下文的四個屬性就能夠了:session

request:請求上下文上的對象。這個對象通常用來保存一些請求的變量。好比method、args、form等。
session:請求上下文上的對象。這個對象通常用來保存一些會話信息。
current_app:返回當前的app。
g:應用上下文上的對象。處理請求時用做臨時存儲的對象。
經常使用的鉤子函數
before_first_request:處理第一次請求以前執行。
例如如下代碼:app

@app.before_first_request def first_request(): print 'first time request'

before_request:在每次請求以前執行。一般能夠用這個裝飾器來給視圖函數增長一些變量。

例如如下代碼:函數

@app.before_request def before_request(): if not hasattr(g,'user'): setattr(g,'user','xxxx')

teardown_appcontext:不論是否有異常,註冊的函數都會在每次請求以後執行。

@app.teardown_appcontext def teardown(exc=None): if exc is None: db.session.commit() else: db.session.rollback() db.session.remove()

template_filter:在使用Jinja2模板的時候自定義過濾器。好比能夠增長一個upper的過濾器(固然Jinja2已經存在這個過濾器,本示例只是爲了演示做用):

@app.template_filter def upper_filter(s): return s.upper()

context_processor:上下文處理器。返回的字典中的鍵能夠在模板上下文中使用。

例如:post

@app.context_processor def my_context_processor(): return {'current_user':'xxx'}

errorhandler:errorhandler接收狀態碼,能夠自定義返回這種狀態碼的響應的處理方法。

例如:

@app.errorhandler(404) def page_not_found(error): return 'This page does not exist',404

 

額外的講解: g

g 也是咱們經常使用的幾個全局變量之一。在最開始這個變量是掛載在 Request Context 下的。可是在 0.10 之後,g 就是掛載在 App Context 下的。可能有同窗不太清楚爲何要這麼作。

首先,說一下 g 用來幹什麼

官方在上下文這一張裏有這一段說明

The application context is created and destroyed as necessary. It never moves between threads and it will not be shared between requests. As such it is the perfect place to store database connection information and other things. The internal stack object is called flask. appctx_stack. Extensions are free to store additional information on the topmost level, assuming they pick a sufficiently unique name and should put their information there, instead of on the flask.g object which is reserved for user code.

大意就是說,數據庫配置和其他的重要配置信息,就掛載 App 對象上。可是若是是一些用戶代碼,好比你不想一層層函數傳數據的話,而後有一些變量須要傳遞,那麼能夠掛在 g 上。

同時前面說了,Flask 並不只僅能夠當作一個 Web Framework 使用,同時也能夠用於一些非 web 的場合下。在這種狀況下,若是 g 是屬於 Request Context 的話,那麼咱們要使用 g 的話,那麼就須要手動構建一個請求,這無疑是不合理的。

g通常用來傳遞上下文的數據,flask裏面有不少鉤子函數,例如before_first_request之類的,g提供了一個方法將數據共享到正常的路由函數裏去。舉個例子,你能夠在before_request裏面作Http Basic Authentication驗證,而後將驗證過的用戶數據存在g裏面,這樣在路由函數裏就能夠直接調用g裏面的用戶數據了,而不用再搞個全局變量。這樣很是方便

Flask中有兩種上下文,請求上下文和應用上下文。

請求上下文(request context)
request和session都屬於請求上下文對象。

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

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

應用上下文(application context)
current_app和g都屬於應用上下文對象。

current_app:表示當前運行程序文件的程序實例。


g:處理請求時,用於臨時存儲的對象,每次請求都會重設這個變量。好比:咱們能夠獲取一些臨時請求的用戶信息。

當調用app = Flask(_name_)的時候,建立了程序應用對象app;
request 在每次http請求發生時,WSGI server調用Flask.call();而後在Flask內部建立的request對象;
app的生命週期大於request和g,一個app存活期間,可能發生屢次http請求,因此就會有多個request和g。
最終傳入視圖函數,經過return、redirect或render_template生成response對象,返回給客戶端。
區別: 請求上下文:保存了客戶端和服務器交互的數據。 應用上下文:在flask程序運行過程當中,保存的一些配置信息,好比程序文件名、數據庫的鏈接、用戶信息等。

上下文對象的做用域
在flask項目中某一個功能中會有多個視圖,那麼from flask import request,current_app,session,g,怎麼保證某次請求的上下文不會被別的視圖拿走呢?

從pycharm中進入globals.py:

_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))

線程有個叫作ThreadLocal的類,也就是一般實現線程隔離的類。而werkzeug本身實現了它的線程隔離類:werkzeug.local.Local。LocalStack就是用Local實現的。


LocalStack是flask定義的線程隔離的棧存儲對象,分別用來保存應用和請求上下文。
它是線程隔離的意思就是說,對於不一樣的線程,它們訪問這兩個對象看到的結果是不同的、徹底隔離的。這是根據pid的不一樣實現的,相似於門牌號。

而每一個傳給flask對象的請求,都是在不一樣的線程中處理,並且同一時刻每一個線程只處理一個請求。因此對於每一個請求來講,它們徹底不用擔憂本身上下文中的數據被別的請求所修改。

而這個LocalProxy 的做用就是能夠根據線程/協程返回對應當前協程/線程的對象,也就是說

線程 A 往 LocalProxy 中塞入 A

線程 B 往 LocalProxy 中塞入 B

不管在是什麼地方,

線程 A 永遠取到得是 A,線程 B 取到得永遠是 B

相關文章
相關標籤/搜索