简而言之,为什么我收到“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
Run Code Online (Sandbox Code Playgroud)
这是我的数据库代码:
#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)
Run Code Online (Sandbox Code Playgroud)
我不确定这是否能真正回答你的问题,但我认为值得一提。
\n在文件的最后一行附近database.py,我建议您不要使用别名db_session.begin(),session因为这样您就会感到困惑,认为它session是类的对象,而它是SessionTransactionSession类的对象,即:
\n\n很大程度上是一个内部对象,在现代使用中为会话事务提供了上下文管理器。会话交易
\n
您可以切换到:
\nwith db_session() as session, session.begin():\n session.add(input)\nRun Code Online (Sandbox Code Playgroud)\n或更短的版本
\nwith db_session.begin():\n db_session.add(input)\nRun Code Online (Sandbox Code Playgroud)\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)\nRun Code Online (Sandbox Code Playgroud)\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 次 |
| 最近记录: |