简而言之,为什么我收到“sqlalchemy.exc.InvalidRequestError:事务已开始。使用 subtransactions=True 允许子事务”错误?
遵循分离和保持会话外部的最佳实践,我foo(input)使用上下文管理器创建,而不是使用 try / except / else。如果我使用foo(user)它而不是它,我会收到上述错误。我的猜测是,这foo()不是提交并关闭连接。然而,文档另有说明。
Flask 文档使用 a,scoped_session但SQLAlchemy 文档表示“但是,强烈建议使用 Web 框架本身提供的集成工具(如果可用),而不是 scoped_session。” 也许这scoped_session会导致请求的线程之间出现错误?
这是我的主要代码:
#__init__.py
import os
from flask import Flask, render_template, redirect, request, url_for
def create_app(test_config=None):
    # create and configure the app
    app = Flask(__name__, instance_relative_config=False)
    app.config.from_object('config.DevelopmentConfig')
    
    # set up extensions
    # all flask extensions must support factory pattern
    # can run these two steps from the cli
    from app.database import init_db
    init_db()
    
    @app.route('/')
    def index():
        return render_template('index.html')
    from app.auth import RegistrationForm
    from app.models import User
    from app.database import db_session, foo
    @app.route('/register', methods=['GET', 'POST'])
    def register():
        form = RegistrationForm(request.form)
        if request.method == 'POST' and form.validate():
            user = User(form.name.data, form.email.data,
                        form.password.data)
            foo(user)
            # try:
            #     db_session.add(user)
            # except:
            #     db_session.rollback()
            #     raise
            # else:
            #     db_session.commit()
            return redirect(url_for('login'))
        return render_template('register.html', form=form)
    
    @app.route('/login', methods=['GET'])
    def login():
        return render_template('login.html')
    
    @app.teardown_appcontext
    def shutdown_session(exception=None):
        db_session.remove()
    
    return app
这是我的数据库代码:
#database.py
from sqlalchemy import create_engine 
from sqlalchemy.orm import scoped_session, sessionmaker 
from sqlalchemy.ext.declarative import declarative_base
_database_uri = os.environ['DATABASE_URL'] engine = create_engine(_database_uri)
db_session = scoped_session(sessionmaker(autocommit=False,
                                         autoflush=False,
                                         bind=engine))
Base = declarative_base() Base.query = db_session.query_property()
def init_db():
    # import all modules here that might define models so that
    # they will be registered properly on the metadata.  Otherwise
    # you will have to import them first before calling init_db()
    import app.models
    Base.metadata.create_all(bind=engine)
def foo(input):
    with db_session.begin() as session:
        session.add(input)
我不确定这是否能真正回答你的问题,但我认为值得一提。
\n在文件的最后一行附近database.py,我建议您不要使用别名db_session.begin(),session因为这样您就会感到困惑,认为它session是类的对象,而它是SessionTransactionSession类的对象,即:
\n\n很大程度上是一个内部对象,在现代使用中为会话事务提供了上下文管理器。会话交易
\n
您可以切换到:
\nwith db_session() as session, session.begin():\n    session.add(input)\n或更短的版本
\nwith db_session.begin():\n    db_session.add(input)\n您还需要将User对象创建包装起来Session.begin()如下所示:
def register():\n    form = RegistrationForm(request.form)\n    if request.method == \'POST\' and form.validate():\n        with db_session.begin():\n            user = User(form.name.data, form.email.data, \n                        form.password.data)\n        foo(user)\n因为User模型只是一个代理对象,它将在幕后实际执行数据库查询。所以A transaction is already begun.在创作过程中。异常本身将在下一次事务查询调用时引发。
\n\n使用 Session 时,考虑将其维护的 ORM 映射对象作为数据库行的代理对象非常有用,这些对象对于 Session 所持有的事务来说是本地的。为了保持对象的状态与数据库中实际的\xe2\x80\x99s 相匹配,有多种事件会导致对象重新访问数据库以保持同步。可以从会话中分离 \xe2\x80\x9c\xe2\x80\x9d 对象并继续使用它们,尽管这种做法有其注意事项。它的目的是,通常,当您想要再次使用分离的对象时,您可以将它们与另一个会话重新关联,以便它们可以恢复表示数据库状态的正常任务。
\n
\n会话基本
作为最后一个问题的附加答案:也许scoped_session 导致请求的线程之间出现错误?
\n不。SQLAlchemyscoped_session实际上是一个辅助函数,充当全局Session对象的注册表。它在多线程应用程序中非常有用,有助于确保Session跨线程使用同一个对象,同时每个线程将自己的数据保存到本地threading.localpython 提供的 api 将自己的数据保存到本地。大多数 Web 框架使用线程策略来同时处理许多 Web 请求,因此大多数框架都提供了一些带有/不带有此帮助器的集成。
| 归档时间: | 
 | 
| 查看次数: | 17106 次 | 
| 最近记录: |