1. 路由解惑
@app.route("/about/") 與 @app.route("/about")
是不一樣的
官方文檔解釋是:前者類似于文件系統(tǒng)的文件夾,后者類似于文件系統(tǒng)唯一的路徑。
也就是說,開發(fā)時使用前者,用戶訪問時可以帶或者不帶后面的斜杠,F(xiàn)lask會默認帶上;使用后者,用戶只能按約定使用一個斜杠。
The canonical URL for the projects endpoint has a trailing slash. It’s similar to a folder in a file system. If you access the URL without a trailing slash, Flask redirects you to the canonical URL with the trailing slash.
The canonical URL for the about endpoint does not have a trailing slash. It’s similar to the pathname of a file. Accessing the URL with a trailing slash produces a 404 “Not Found” error. This helps keep URLs unique for these resources, which helps search engines avoid indexing the same page twice.
2. Blueprint Resource Folder
Blueprint的第二個參數(shù)通常是__name__.這個參數(shù)指定blueprint對應的邏輯python包或模塊。如果它指向一個實際的python包(這個包在文件系統(tǒng)中)是資源目錄(resource folder)如果這是個模塊,這個模塊所在的包,將會是resource folder.
可以訪問Blueprint.root_path屬性查看是什么文件夾:
>>> simple_page.root_path
'/users/username/TestProject/yourapplication'
可以使用open_resource()函數(shù)快速打開資源文件夾:
with simple_page.open_resource('static/style.css') as f:
code = f.read()
3. Static Files
一個blueprint可以通過使用static_folder參數(shù)提供文件系統(tǒng)上的路徑暴露一個含有靜態(tài)文件的文件夾。
admin = Blueprint('admin',name, static_folder= 'static' )
4. Templates
5. The Application Context(應用上下文)
應用上下文跟蹤應用級別的數(shù)據(jù)在一個請求,CLI 命令或者其他活動內(nèi)。不是經(jīng)過應用給每一個函數(shù),而是使用current_app和g代替。
這一點和請求上下文很相似,請求上下文跟蹤一個請求級別的數(shù)據(jù)在一個請求內(nèi)。當一個請求上下文被入棧相應的應用上下文被入棧。
5.1 上下文的意圖
Flask 應用對象有屬性,比如config,對范文視圖函數(shù)和CLI command很有用。然而,在你的項目的模塊中引入app實例很容易引起循環(huán)引入的問題。當使用app factory pattern 或者寫一個可復用的blueprints或擴展就沒有必要導入一個app實例
Flask 通過application context來解決這個問題。不是直接使用app,而是使用current_app proxy(代理),
它指向處理當前活動的應用。
Flask 自動入棧(push)一個應用上下文當處理請求的時候。在一個請求內(nèi)的view functions,error handlers and other functions將會訪問current_app.
Flask也將自動入棧一個app 上下文當運行一個用Flask.cli(使用@app.cli.command())注冊的CLI 命令時。
5.2 上下文的生命周期
應用上下文的創(chuàng)造和銷毀隨需要進行的。當一個Flask應用開始處理一個請求,它入棧一個應用上下文和一個請求上下文。當一個請求結(jié)束時它出棧一個請求上下文然后一個應用上下文。經(jīng)典地,一個應用上下文和一個請求具有相同的生命周期。
5.3 手動入棧一個上下文(Manually Push a Context)
如果你試圖訪問一個current_app?;蛘咂渌魏文闶褂玫?,超出應用上下文的,你將會得到一個錯誤信息:
RuntimeError: Working outside of application context.
This typically means that you attempted to use functionality that
needed to interface with the current application object in some way.
To solve this, set up an application context with app.app_context().
如果你正在配置應用時看到這個錯誤,你可以手動入棧一個上下文,因為你可以直接訪問app。 在一個 with 塊中使用app_context(),運行在這個塊中的一切都可以訪問current_app
如果你在和配置無關的地方看到這個錯誤,最可能暗示你應該移動代碼到一個試圖函數(shù)里或CLI 命令里。
5.4 存儲數(shù)據(jù)
在一個請求或CLI command內(nèi),應用上下文是一個存儲數(shù)據(jù)的好地方。Flask 提供 g object為了這個意圖。這是一個簡單的命名空間和應用上下文有相同的生命周期。
Note:
g 表示"global" ,但它指的是上下文中的全部數(shù)據(jù)。上下文結(jié)束后
g中的數(shù)據(jù)將會丟失,而且也不是一個適合在請求之間存儲數(shù)據(jù)的好地方。使用session或者數(shù)據(jù)庫來存儲跨請求的數(shù)據(jù)。
g 常用來在一個請求內(nèi)管理資源。
- get_X() 創(chuàng)造資源X。如果它不存在,緩存它作為g.X
- teardown_X() 關閉資源或其他解除資源。它被注冊為teardown_appcontext()處理器
for example,你可以使用這個模式管理數(shù)據(jù)庫連接:
from flask import g
def get_db():
if 'db' not in g:
g.db = connect_to_database()
return g.db
@app.teardown_appcontext
def teardown_db():
db = g.pop('db', None)
if db is not None:
db.close()
6. (The Request Context)請求上下文
請求上下文在一個請求內(nèi)跟蹤請求級的數(shù)據(jù)。不是將請求對象傳遞給每一個函數(shù),request和session代理是可以訪問的。
請求上下文的意圖
當Flask應用處理請求的時候,它創(chuàng)建一個Request對象根據(jù)環(huán)境它從WSGI服務器接收。因為worker(thread, process, or coroutine depending on the server)一次只能處理一個請求,請求數(shù)據(jù)可以被認為是這個worker的全局數(shù)據(jù)。為此Flask 使用術(shù)語 context local
Flask 自動入棧一個請求上下文。在一個請求內(nèi)的view functions, error handlers,和其他functions將可以使用request代理,它將指向當前請求的請求對象。
6.1 上下文的生命周期
當一個Flask應用開始處理請求的時候,它自動入棧一個請求上下文,也入棧一個應用上下文。當請求結(jié)束時,它出棧一個請求上下文之后出棧一個應用上下文。
對每一個線程(或其他工作類型),上下文是獨一無二的。request不能被帶到其他線程,其他線程會有一個不同的上下文棧而且不知道父線程指向的request.
Context locals在Werkzeug中實現(xiàn)。
6.2 手動入棧一個上下文
如果你嘗試訪問一個request,或者其他使用request,你將會得到一個錯誤信息:
RuntimeError: Working outside of request context.
This typically means that you attempted to use functionality that
needed an active HTTP request. Consult the documentation on testing
for information about how to avoid this problem.
這經(jīng)常出現(xiàn)在測試代碼中,測試代碼期望一個激活的request.一個選項是使用test_client來模擬一個完整的request.或者你可以在with塊中使用test_request_context(),運行在塊中的一切都可以訪問request,用測試數(shù)據(jù)填充。
def generate_report(year):
format = request.args.get('format')
...
with app.test_request_context(
'/make_report/2017', data={'format': 'short'}):
generate_report()
如果你在和測試無關的地方看到這個錯誤,很可能你應該把代碼放到視圖函數(shù)里。
6.3 How the Context Works
Flask.wsgi_app()方法被調(diào)用來處理每一個請求。它在請求期間管理上下文。內(nèi)部,請求上下文和應用上下文的工作形式為棧,_request_ctx_stack 和 _app_ctx_stack.當上下文被入棧,依賴他們的棧是可得到的,并且指向來自棧頂上下文的信息。
當請求開始的時候,一個RequestContext被創(chuàng)建和被入棧,它創(chuàng)建并入棧一個AppContext如果這個應用的上下文沒有已經(jīng)存在上下文頂部。當這些上下文都被入棧,current_app, g, request, session代理就可以得到對原本的的線程處理請求。
因為上下文是棧,在一個請求內(nèi)其他上下文可能被入棧從而改變代理。盡管這不是一個通用的模式,它仍然可以被使用在高級應用中,舉例,在內(nèi)部重定向或鏈到不同的應用。
請求被調(diào)配和相應被注冊和發(fā)送后,請求上下文被出棧,之后出棧應用上下文。在他們被彈出之前,teardown_request()和tear_appcontext()函數(shù)會被執(zhí)行。這些執(zhí)行盡管未處理的異常發(fā)生在調(diào)配期間。
6.4 Callbacks and Errors
Flask 在多個階段調(diào)度一個請求可以影響request, response 和怎么處理一個錯誤。這些上下文被激活在所有階段。
一個Blueprint可以添加處理器對于這些事件。一個blueprint的處理器將會運行,如果這個blueprint屬于匹配這個請求的路徑。
每個請求之前,
before_request()函數(shù)被調(diào)用。如果這些函數(shù)當中的任一個返回一個值,其他的函數(shù)被跳過。這個返回值被當作響應,視圖函數(shù)不再調(diào)用。如果
before_request()函數(shù)沒有返回一個響應,匹配路由的視圖函數(shù)被調(diào)用然后返回一個響應。視圖函數(shù)的返回值被轉(zhuǎn)換成一個實際的response object 并傳遞給after_request()函數(shù)。每個函數(shù)返回一個被改進的或新的response object.
response 返回之后,上下文被出棧,調(diào)用teardown_request()和teardown_appcontext()函數(shù)。這些函數(shù)被調(diào)用盡管上面那些步驟拋出一個未處理的異常。
如果一個異常在teardown函數(shù)之前被拋出,F(xiàn)lask嘗試使用errorhandler()匹配它來處理異常和返回一個響應。如果沒有發(fā)現(xiàn)error handler,或者handler本身拋出一個異常,F(xiàn)lask 返回一個通用的(generic)的500 Internal Server Error響應。teardown函數(shù)仍然被調(diào)用,并被傳遞給異常對象。
如果啟動debug 模式,未處理的異常不會被轉(zhuǎn)換成500響應而是傳播給WSGI 服務器。這允許開發(fā)服務器展示對應的debugger with traceback.
6.5 Teardown Callbacks
teardown callbacks是獨立與請求調(diào)度的,當context被出棧時調(diào)用。函數(shù)被調(diào)用盡管有一個未處理的異常在調(diào)配期間,手動入棧一個上下文。這意味著沒有保證任何其他請求調(diào)度先執(zhí)行。確保寫這些函數(shù)以一種不依賴其他回調(diào)的方式并且不會失敗。
在測試期間,他可以很有用對于推遲上下文出棧,這樣他們的數(shù)據(jù)可以被其他測試函數(shù)訪問。使用test_client()作為一個with 塊保護上下文直到with 塊 退出。
7 Config
關于Flask的官config, 官方文檔
寫得很好。