Flask 源碼閱讀筆記我覺得我已經(jīng)養(yǎng)成了一個壞習慣,在使用一個框架過程中對它的內(nèi)部原理非常感興趣,有時候需要花不少精力才 明白,這也導致了學習的緩慢,但換來的是對框架的內(nèi)部機理的熟悉,正如侯捷所說,源碼面前,了無秘密。這也是 本文產(chǎn)生的直接原因。 一.flask session原理 flask的session是通過客戶端的cookie實現(xiàn)的,不同于diango的服務器端實現(xiàn),flask通過itsdangerous這個苦 將session的內(nèi)容序列化到瀏覽器的cookie,當瀏覽器再次請求時將反序列化cookie內(nèi)容,也就得到我們的session內(nèi)容。 比如說session['name']='kehan',客戶端session如下, 我們來解密這個cookie存儲了什么值 該cookie通過.分割,分成了三部分:內(nèi)容序列化+時間+防篡改值 通過第一部分我們就獲得了session['name']的值,我們看看第二部分 第二部分保存的是時間,itsdangerous庫為了減少時間戳的值,之前減掉了118897263,所以我們要加上。 這個時間flask是用來判斷session是否過期使用的。 第三部分是session值和時間戳以及我們SECRET_KEY的防篡改值,通過HMAC算法簽名。也就是說即使你修改了 前面的值,由于簽名值有誤,flask不會使用該session。所以一定要保存好SECRET_KEY,以及確保它的復查度, 不然一旦讓別人知道了SECRET_KEY,就可以通過構造cookie偽造session值,這是很恐怖的一件事。 我們知道一般為了保護session,所以session的生成還會包含客戶端user_agent,remete_addr等,如果你覺得使用 flask提供的保護力度不夠,可以使用flask_login這個擴展,一幫在flask使用認證時都會使用這個擴展,簡單易用, 還提供了更加強度的session保護。 二. flask擴展import 原理 我喜歡flask的一個理由就是導入簡單,非擴展的都可以通過from flask導入,擴展的都是通過from flask.ext. 導入,非常簡潔。用django的過程中,經(jīng)常不記得該從哪里導入,在flask的世界里,你無需煩惱。那么flask的擴展 導入原理是什么呢? 主要通過sys.meta_path實現(xiàn)的 當導入 from falsk.ext.example import E是將會執(zhí)行flask/ext/__init__.py
比如當我們 from flask.ext.script import Manager時 會調(diào)用find_module('flask.ext.script'),prefinx是flask.ext所以將會調(diào)用load_module() 此時將會嘗試import flask_script模塊或flaskext.script
三. flask sqlalchemy原理 sqlalchemy是python中最強大的orm框架,無疑sqlalchemy的使用比django自帶的orm要復雜的多, 使用flask sqlalchemy擴展將拉近和django的簡單易用距離。 先來說兩個比較重要的配置 app.config['SQLALCHEMY_ECHO'] = True =》配置輸出sql語句 app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True =》每次request自動提交db.session.commit(), 如果有一天你發(fā)現(xiàn)別的寫的視圖中有db.session.add,但沒有db.session.commit,不要疑惑,他肯定配置了上面 的選項。 這是通過app.teardown_appcontext注冊實現(xiàn)
上面self.session.remove()表示每次請求后都會銷毀self.session,為什么要這么做呢? 這就要說說sqlalchemy的session對象了。 from sqlalchemy.orm import sessionmaker session = sessionmaker() 一幫我們會通過sessionmaker()這個工廠函數(shù)創(chuàng)建session,但這個session并不能用在多線程中,為了支持多線程 操作,sqlalchemy提供了scoped_session,通過名字反映出scoped_session是通過某個作用域?qū)崿F(xiàn)的 所以在多線程中一幫都是如下使用session from sqlalchemy.orm import scoped_session, sessionmaker session = scoped_session(sessionmaker()) 我們來看看scoped_session是如何提供多線程環(huán)境支持的
函數(shù))而scopefunc就是能產(chǎn)生某個作用域的函數(shù),如果不提供將使用ThreadLocalRegistry
調(diào)用__call__的時機,但是在flask_sqlalchemy中并沒有使用ThreadLocalRegistry,創(chuàng)建scoped_session過程如下
如果你看過前一篇文章你就知道__ident_func__其實就是在多線程中就是thrading.get_ident,也就是線程id 我們看看ScopedRegistry是如何通過_操作的
魔法了,和flask cookie,g有異曲同工之妙,這里有兩個小問題? 1.flask_sqlalchemy能否使用ThreadLocalRegistry? 大部分情況都是可以的,但如果wsgi對多并發(fā)使用的是greenlet的模式就不適用了 2.上面create_scoped_session中partial是干嘛的? 前面我們說過scoped_session的session_factory是可調(diào)用對象,但_SignallingSession類并沒有定義__call__,所以通過partial支持 到這里你就知道為什么每次請求結束要self.session.remove(),不然為導致存放session的字段太大 這里說一下對db.relationship lazy的理解,看如下代碼
lazy:dynamic => role.users不會返回User的列表, 返回的是sqlalchemy.orm.dynamic.AppenderBaseQuery對象 當執(zhí)行role.users.all()是才會真正執(zhí)行sql,這樣的好處就是可以繼續(xù)過濾 lazy:select => role.users直接返回User實例的列表,也就是直接執(zhí)行sql 注意:db.session.commit只有在對象有變化時才會真的執(zhí)行update 四. flask moment原理 flask moment簡單封裝了moment.js,moment.js通過js提供了對時間的支持,感興趣的童鞋可以關注下, 功能非常強大。 flask moment原理很簡單,使用帶有時間的格式話字符串在dom加載后,使用moment.js處理一下, 該步操作有moment.include_moment()完成。如果使用其它語言,如中文,調(diào)用moment.lang('zh-cn') 如果使用了flask bootstrap,只需要在最后添加以下代碼即可(需要jquery支持)
單位為分鐘 {{ moment(current_time).fromNow(refresh=True) }} 看上面我們知道,flask moment在模板中導入了moment這個對象,這是如何實現(xiàn)的呢?
def render_template(template_name_or_list, **context): ctx.app.update_template_context(context) 在render_template中會把前面注冊的變量添加到context,所以在模板中就可以使用moment了, 而flask bootstrap是通過app.jinja_env.globals['bootstrap_find_resource'] = bootstrap_find_resource實現(xiàn)的 我們知道flask在初始化jinja環(huán)境的時候就將request,g,session等注入到全局了
哈哈,認真看上面rv.globals.update的注釋部分能大概明白。 flask模板可以使用宏,需要使用import導入,此時導入的模板不能訪問不能訪問當前模板的本地變量,只能使用全局變量。 這也就是為什么global中有g,request,session的理由,也即是為了支持在宏中使用g對象 來自:http://blog.csdn.net/yueguanghaidao/article/details/40016235 |
|
|
來自: java_laq小館 > 《Python》