【问题标题】:Flask-SQLAlchemy fails to update data in while in threadFlask-SQLAlchemy 在线程中无法更新数据
【发布时间】:2020-03-30 06:05:24
【问题描述】:

我正在构建一个应用程序,用户偶尔会在其中启动一个运行时间较长的进程。运行时,该进程将提交对数据库条目的更新。

由于这个过程需要一些时间,我使用threading 模块来执行它。但是在线程中更新的值永远不会真正提交。

一个例子:

from flask import Flask, url_for, redirect
from flask_sqlalchemy import SQLAlchemy
import time, threading, os

if os.path.exists('test.db'): os.remove('test.db')

app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
db = SQLAlchemy(app)

class Item(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    value = db.Column(db.Integer)
    def __init__(self, value): self.value = value

db.create_all()

item = Item(1)
db.session.add(item)
db.session.commit()

@app.route('/go', methods=['GET'])
def go():
    def fun(item):
        time.sleep(2)
        item.value += 1
        db.session.commit()
    thr = threading.Thread(target=fun, args=(item,))
    # thr.daemon = True
    thr.start()
    return redirect(url_for('view'))

@app.route('/view', methods=['GET'])
def view(): return str(Item.query.get(1).value)

app.run(host='0.0.0.0', port=8080, debug=True)

我的期望是该项目的值将在两秒后异步更新(当fun 完成时),并且对/view 的额外请求将显示更新的值。但这永远不会发生。我不是线程模块中发生的事情的专家;我错过了什么吗?

我已经尝试设置thr.daemon=True,正如一些帖子中指出的那样;但事实并非如此。我找到的最接近的 SO 帖子是 this one;该问题没有最小且可验证的示例,也没有得到回答。

【问题讨论】:

标签: python multithreading flask sqlalchemy flask-sqlalchemy


【解决方案1】:

我猜这是因为会话是本地线程的,正如the documentation 中提到的那样。在您的情况下,item 是在一个线程中创建的,然后传递给一个新线程以直接修改。

您可以按照文档中的建议使用范围会话,或者只是更改 URI 配置以绕过此行为:

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db?check_same_thread=False'

【讨论】:

    【解决方案2】:

    经过一些调试,我想出了一个解决方案;虽然我仍然不明白这个问题。它与引用数据库对象的变量有关。如果fun 更新查询返回的对象,它会按预期工作:

    def fun(item_id):
        time.sleep(2)
        Item.query.get(item_id).value += 1
        db.session.commit()
    

    在上下文中:

    from flask import Flask, url_for, redirect
    from flask_sqlalchemy import SQLAlchemy
    import time, threading, os
    
    if os.path.exists('test.db'): os.remove('test.db')
    
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///test.db'
    db = SQLAlchemy(app)
    
    class Item(db.Model):
        id = db.Column(db.Integer, primary_key=True)
        value = db.Column(db.Integer)
        def __init__(self, value): self.value = value
    
    db.create_all()
    
    item = Item(1)
    db.session.add(item)
    db.session.commit()
    
    @app.route('/go', methods=['GET'])
    def go():
        def fun(item_id):
            time.sleep(2)
            Item.query.get(item_id).value += 1
            db.session.commit()
        thr = threading.Thread(target=fun, args=(item.id,))
        # thr.daemon = True
        thr.start()
        return redirect(url_for('view'))
    
    @app.route('/view', methods=['GET'])
    def view(): return str(Item.query.get(1).value)
    
    app.run(host='0.0.0.0', port=8080, debug=True)
    

    如果有人知道这里到底发生了什么,我会很高兴的!

    【讨论】:

    • 有趣的是,您应该告诉 sqlalchemy 您有一个必须更新的对象:item.value += 1 db.session.add(item) db.session.commit() 它不会自动确定要更新的项目。在第二个示例中,我假设使用 .query 它会创建一个带有更新的查询(从未以这种方式使用过)
    猜你喜欢
    • 2017-12-15
    • 1970-01-01
    • 1970-01-01
    • 2022-08-15
    • 2020-10-03
    • 2021-11-02
    • 2022-01-09
    • 2017-04-08
    • 1970-01-01
    相关资源
    最近更新 更多