Flask零基礎到項目實戰(七)請求方法、g對象和鈎子函數
一、get方法
二、post方法
post請求在模板中要注意幾點:
- 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>
三、g對象
g:global
1. g對象是專門用來保存用戶的數據的。
2. g對象在一次請求中的所有的代碼的地方,都是可以使用的。
使用步驟:
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中的函數
#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()
測試:
現在在瀏覽器里面,訪問兩次服務器,效果如下:
四、鈎子函數
在程序正常運行的時候,程序按照A函數—->B函數的順序依次運行;鈎子函數可以插入到A函數到B函數運行中間從而,程序運行順序變成了A—->鈎子函數—->B函數。
Flask項目中有兩個上下文,一個是應用上下文(app),另外一個是請求上下文(request)。請求上下文request和應用上下文current_app都是一個全局變量。所有請求都共享的。Flask有特殊的機制可以保證每次請求的數據都是隔離的,即A請求所產生的數據不會影響到B請求。所以可以直接導入request對象,也不會被一些臟數據影響了,並且不需要在每個函數中使用request的時候傳入request對象。這兩個上下文具體的實現方式和原理可以沒必要詳細了解。只要了解這兩個上下文的四個屬性就可以了:
request:請求上下文上的對象。這個對象一般用來保存一些請求的變量。比如method、args、form等。
session:請求上下文上的對象。這個對象一般用來保存一些會話信息。
current_app:返回當前的app。
g:應用上下文上的對象。處理請求時用作臨時存儲的對象。
常用的鈎子函數
before_first_request:處理第一次請求之前執行。
例如以下代碼:
@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:上下文處理器。返回的字典中的鍵可以在模板上下文中使用。
例如:
@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 的話,那么就需要手動構建一個請求,這無疑是不合理的。
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