国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

關(guān)于Flask-SQLAlchemy事務(wù)提交有趣的探討

BlackHole1 / 1721人閱讀

摘要:最近在開發(fā)的時(shí)候遇到這樣一個(gè)問題我就好奇了這樣還報(bào)不在中的錯(cuò)沒有顯示調(diào)用啊加一行測(cè)試無(wú)奈,一個(gè)一個(gè)翻到之間調(diào)用的每一個(gè)函數(shù),終于在找到可疑點(diǎn)但是這里也沒有顯式提交。為什么接下來(lái)總結(jié)下大神們的探討。

最近在開發(fā)mdwiki的時(shí)候遇到這樣一個(gè)問題.Post is unbond to session.
我就好奇了

post=Post.query.filter_by(location=location).first()
abspath=util.getAbsPostPath(post.location)
tagsList=[]
...
print(post in session) #False
post.tags=tagsList

這樣還報(bào)post不在session中的錯(cuò)?沒有顯示調(diào)用db.session.commit()啊.
加一行測(cè)試:
print(post in session) #False
無(wú)奈,一個(gè)一個(gè)翻post=Post.query.filter_by(location=location).first()到post.tags=tagsList之間調(diào)用的每一個(gè)函數(shù),終于在util.getAbsPostPath找到可疑點(diǎn)

def getAbsPostPath(location):
    with current_app.app_context():              
        abspath=os.path.join(current_app.config["PAGE_DIR"],location.replace("/",os.sep))+".md"
    return abspath

但是這里也沒有顯式提交。只是多push了一個(gè)app_context,也不至于這樣吧?
無(wú)奈之下查看Flask-SQLAlchemy源碼,還好這貨只有兩個(gè)文件,比較少。
有這么一段:

        # 0.9 and later
        if hasattr(app, "teardown_appcontext"):
            teardown = app.teardown_appcontext
        # 0.7 to 0.8
        elif hasattr(app, "teardown_request"):
            teardown = app.teardown_request
        # Older Flask versions
        else:
            if app.config["SQLALCHEMY_COMMIT_ON_TEARDOWN"]:
                raise RuntimeError("Commit on teardown requires Flask >= 0.7")
            teardown = app.after_request

        @teardown
        def shutdown_session(response_or_exc):
            if app.config["SQLALCHEMY_COMMIT_ON_TEARDOWN"]:
                if response_or_exc is None:
                    self.session.commit()
            self.session.remove()
            return response_or_exc

這下明白了,原理是它監(jiān)聽了app.teardown_appcontext事件,在該事件發(fā)生時(shí)會(huì)調(diào)用
self.session.remove()移除session。這樣一來(lái)把這一行注釋掉,直接使用config模塊就解決問題了

def getAbsPostPath(location):
       # with current_app.app_context(): 
       abspath=os.path.join(config.PAGE_DIR,location.replace("/",os.sep))+".md"  

但同時(shí)看到了這個(gè)選項(xiàng)SQLALCHEMY_COMMIT_ON_TEARDOWN,是不是Flask-SQLAlchemy可以配置請(qǐng)求執(zhí)行完邏輯之后自動(dòng)提交,而不用我們每次都手動(dòng)調(diào)用session.commit()?通過源碼看答案是肯定的。
但是好奇的我還是google之,然后在github上看到了這樣幾段有趣的討論:先貼地址
https://github.com/mitsuhiko/...
https://github.com/mitsuhiko/...
https://github.com/rosariomgo...

然后在官網(wǎng)看到這樣一段:

Consider SQLALCHEMY_COMMIT_ON_TEARDOWN harmful and remove from docs.

什么?考慮移除這一特性?
剛剛知道這么方便的特性,準(zhǔn)備用來(lái)著,就要被移除?更何況源碼中也沒有提示要移除啊。
其實(shí)是這樣的,作者準(zhǔn)備在3.0版本移除SQLALCHEMY_COMMIT_ON_TEARDOWN這一特性,目前自2.1以后從文檔中移除了相關(guān)介紹。

為什么?接下來(lái)總結(jié)下大神們的探討。

mattupstate commented on 31 Jan 2015: I"d guess that the reason is due
to the teardown_appcontext callback carrying a bug that, even if you
catch an exception during the app context, the response_or_exc will
never be None. In other words, teardown_appcontext suffers from a
general Python exception handling bug.

這位mattupstate說teardown_appcontext回調(diào)存在一個(gè)bug,就是即使你正確地捕獲了所有的bug,但是回調(diào)函數(shù)的第一個(gè)參數(shù)response_or_exc仍然不會(huì)為None。這一點(diǎn)令人費(fèi)解。于是我試驗(yàn)了一發(fā),包括沒有bug的情形,主動(dòng)拋出并捕獲的情形,以及after_request中捕獲并拋出的情形,都發(fā)現(xiàn)response_or_exc為None,沒有重現(xiàn)他所說的。why?好想知道為什么。猜測(cè)可能是我Python版本,F(xiàn)lask版本的關(guān)系?繼續(xù)看吧

immunda commented on 3 Feb 2015 Sorry for the silence on this. Yep,
that"s the motivation, moving away from the (flawed) magic. I"m
waiting to deprecate it entirely (3.0), because there"s plans to
introduce a more explicit transaction decorator first.

我去,連Flask-SQLAlchemy作者都支持這一觀點(diǎn),好吧,雖然我沒有重現(xiàn)該問題,但是還是就這么認(rèn)為吧,不用這個(gè)特性了。但是還是好奇地看了一下其他的觀點(diǎn)。

原來(lái)實(shí)際上問題是這樣的,見https://github.com/mitsuhiko/...

先貼上FLask-SQLAlchemy那部分代碼:

       @teardown
        def shutdown_session(response_or_exc):
            if app.config["SQLALCHEMY_COMMIT_ON_TEARDOWN"]:
                if response_or_exc is None:
                    self.session.commit()
            self.session.remove()
            return response_or_exc

如果在app.teardown_request中或者在self.session.commit()時(shí)發(fā)生異常,而這個(gè)異常在這里并沒有被捕獲,那么self.session.remove()也就沒有執(zhí)行,那么這就會(huì)影響到下一個(gè)請(qǐng)求,下一個(gè)請(qǐng)求獲取到的session其實(shí)是上一個(gè)帶回滾狀態(tài)的session,從而導(dǎo)致請(qǐng)求沒有按預(yù)期效果執(zhí)行而失敗。至此問題算明白了。并不是mattupstate這哥們形容的那樣。那么這應(yīng)該是flask實(shí)現(xiàn)機(jī)制導(dǎo)致的吧。繼續(xù)挖。
https://github.com/pallets/fl...
http://stackoverflow.com/ques...
這哥們garaden給Flask提交了代碼合并請(qǐng)求,關(guān)鍵部分如下

+    def wrap_teardown_func(teardown_func):
 +        @wraps(teardown_func)
 +        def log_teardown_error(*args, **kwargs):
 +            try:
 +                teardown_func(*args, **kwargs)
 +            except Exception as exc:
 +                app.logger.exception(exc)
 +        return log_teardown_error
 +
 +    if app.teardown_request_funcs:
 +        for bp, func_list in app.teardown_request_funcs.items():
 +            for i, func in enumerate(func_list):
 +                app.teardown_request_funcs[bp][i] = wrap_teardown_func(func)
 +    if app.teardown_appcontext_funcs:
 +        for i, func in enumerate(app.teardown_appcontext_funcs):
 +            app.teardown_appcontext_funcs[i] = wrap_teardown_func(func)

如果合并了這部分代碼之后,那么以后注冊(cè)app.teardown_request和app.teardown_appcontext,時(shí)異常將會(huì)自動(dòng)被捕獲。這在https://github.com/pallets/fl...可以看到新版本Flask已經(jīng)合并了這部分代碼,不存在該問題了。
后面討論看到

Recently PR pallets/flask#1822 got merged into Flask. Will this maybe
change the fact whether SQLALCHEMY_COMMIT_ON_TEARDOWN will still be
removed in future?

但這對(duì)于解決FLask-SQLAlchemy中的問題好像還是沒有幫助?是不是我理解錯(cuò)了?如果session.commit發(fā)生異常,session.remove這樣還是不會(huì)執(zhí)行?
后面看了https://github.com/pallets/fl...中的代碼,優(yōu)化了application context從棧中pop的邏輯,這次的代碼提交確保了tear_down回調(diào)處理發(fā)生異常時(shí)不會(huì)導(dǎo)致application context無(wú)法從棧中彈出而影響后續(xù)請(qǐng)求。這下大致明白了。Flask-SQLAlchemy中的db.session依賴于Application Context,所以如果這次Flask能確保無(wú)論如何最后會(huì)正確彈出application context,那么db.session也隨之銷毀了,那就不存在后續(xù)的影響了。但是,最后這句話我也不敢保證,只能是猜想。

所以,言歸正傳,如果不用SQLALCHEMY_COMMIT_ON_TEARDOWN這一特性,那么我們?cè)趺创_保每次自動(dòng)提交session呢?
第一種:不是自動(dòng),全手動(dòng)模式commit(),看討論還是有很多人喜歡這種方式的,不過我討厭每次都調(diào)用commit()
第二種:在after_request中進(jìn)行提交commit,在teardown_request進(jìn)行remove
雖說Flask已經(jīng)修正不需要捕獲也可以,但是為了編碼的優(yōu)雅(暫時(shí)找不到好點(diǎn)的詞),還是在dbsession_clean中進(jìn)行了異常捕獲。

@app.after_request
def after_clean(resp,*args,**kwargs):
    db.session.commit()
    return resp
@app.teardown_request
def dbsession_clean(exception=None):
    try:
        db.session.remove()
    finally:
        pass

第三種:使用自定義裝飾器

def route(app_or_sub,rule,**options):
    def decorator(f):
        @wraps(f)
        def decorated_view(*args,**kwargs):
            res=f(*args,**kwargs)
            db.session.commit()
            return res
        endpoint = options.pop("endpoint", None)
        app_or_sub.add_url_rule(rule, endpoint, decorated_view, **options)
        return decorated_view
    return decorator

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/38270.html

相關(guān)文章

  • Flask 插件系列 - Flask-SQLAlchemy

    摘要:程序中最常用的莫過于關(guān)系型數(shù)據(jù)庫(kù)了,也稱數(shù)據(jù)庫(kù)。對(duì)象是類的實(shí)例,表示程序使用的數(shù)據(jù)庫(kù)。本文由發(fā)表于個(gè)人博客,采用自由轉(zhuǎn)載保持署名非商用禁止演繹協(xié)議發(fā)布。非商業(yè)轉(zhuǎn)載請(qǐng)注明作者及出處。本文標(biāo)題為插件系列本文鏈接為更多閱讀 簡(jiǎn)介 Web 開發(fā)中,一個(gè)重要的組成部分便是數(shù)據(jù)庫(kù)了。Web 程序中最常用的莫過于關(guān)系型數(shù)據(jù)庫(kù)了,也稱 SQL 數(shù)據(jù)庫(kù)。另外,文檔數(shù)據(jù)庫(kù)(如 mongodb)、鍵值對(duì)數(shù)據(jù)...

    LoftySoul 評(píng)論0 收藏0
  • flask-sqlalchemy操作(基礎(chǔ))

    摘要:以下內(nèi)容介紹了的基礎(chǔ)查詢語(yǔ)句,下篇文章將介紹其高級(jí)查詢聚合自關(guān)聯(lián)連接子查詢等模型類用戶表地址信息關(guān)聯(lián)表商品信息訂單表增一對(duì)一構(gòu)建對(duì)象添加對(duì)象提交事務(wù)一對(duì)多主表子表子表賦值對(duì)象添加提交多對(duì)多生成或獲取商品對(duì)象生成訂單對(duì)象訂單表與商品表關(guān)聯(lián)添加 以下內(nèi)容介紹了Sqlalchemy的基礎(chǔ)查詢語(yǔ)句,下篇文章將介紹其高級(jí)查詢(聚合、自關(guān)聯(lián)、連接、子查詢等) 模型類 # 用戶表 class Use...

    Cciradih 評(píng)論0 收藏0
  • Flask Web Development —— 數(shù)據(jù)庫(kù)(中)

    摘要:關(guān)系關(guān)系數(shù)據(jù)庫(kù)通過使用關(guān)系在不同的表中建立連接。以下部分將介紹最常見的數(shù)據(jù)庫(kù)操作。如果數(shù)據(jù)庫(kù)已存在函數(shù)不會(huì)重新創(chuàng)建或更新數(shù)據(jù)庫(kù)表。到目前為止對(duì)象只存于中,他們還沒有被寫入數(shù)據(jù)庫(kù)。數(shù)據(jù)庫(kù)會(huì)話也叫事務(wù)。刪除行數(shù)據(jù)庫(kù)會(huì)話同樣有方法。 7、關(guān)系 關(guān)系數(shù)據(jù)庫(kù)通過使用關(guān)系在不同的表中建立連接。圖像5-1的關(guān)系圖表達(dá)了用戶和用戶角色之間的簡(jiǎn)單關(guān)系。這個(gè)角色和用戶是一對(duì)多關(guān)系,因?yàn)橐粋€(gè)角色可以從屬于...

    April 評(píng)論0 收藏0
  • Flask Web Development —— 數(shù)據(jù)庫(kù)(中)

    摘要:關(guān)系關(guān)系數(shù)據(jù)庫(kù)通過使用關(guān)系在不同的表中建立連接。以下部分將介紹最常見的數(shù)據(jù)庫(kù)操作。如果數(shù)據(jù)庫(kù)已存在函數(shù)不會(huì)重新創(chuàng)建或更新數(shù)據(jù)庫(kù)表。到目前為止對(duì)象只存于中,他們還沒有被寫入數(shù)據(jù)庫(kù)。數(shù)據(jù)庫(kù)會(huì)話也叫事務(wù)。刪除行數(shù)據(jù)庫(kù)會(huì)話同樣有方法。 7、關(guān)系 關(guān)系數(shù)據(jù)庫(kù)通過使用關(guān)系在不同的表中建立連接。圖像5-1的關(guān)系圖表達(dá)了用戶和用戶角色之間的簡(jiǎn)單關(guān)系。這個(gè)角色和用戶是一對(duì)多關(guān)系,因?yàn)橐粋€(gè)角色可以從屬于...

    keelii 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<