【问题标题】:SQLAlchemy sessions errorSQLAlchemy 会话错误
【发布时间】:2023-04-10 19:20:01
【问题描述】:

背景:Flask / Flask-SQLAlchemy / Flask-WTF,使用声明性和作用域会话

简单的POST操作:

@tas.route('/order_add', methods=['GET', 'POST'])  
def tas_order_add():  
    if request.method == 'POST':
        order_form = OrderForm()
        if order_form.validate_on_submit():
            order = Order()
            order_form.populate_obj(order)
            db_session.add(order)
            db_session.commit()

现在尝试运行它我得到一个错误:

InvalidRequestError:对象“”已附加到会话“1”(这是“2”)

将添加更改为合并可以解决问题,但是:

  • 我不知道为什么我刚刚启动它时必须合并一个对象
  • 如果我确实将添加更改为合并并尝试在行中定义其中一个属性

    order = Order()
    order_form.populate_obj(order)
    order.order_status = OrderStatus.query.filter(OrderStatus.code=='PLACED').first()
    db_session.merge(order)
    db_session.commit()
    

    我在 OrderStatus 对象上遇到了同样的错误

    InvalidRequestError:对象“”已附加到会话“2”(这是“1”)

谁能指出我做错了什么,因为它让我发疯。我确实有一些使用 SQLAlchemy 的经​​验,但这是我第一次看到这种行为,我无法确定问题所在。

搜索所有我发现的是双数据库会话初始化的问题,但我不相信这是这种情况。

编辑

db_session 定义在单独的文件 database.py 中,内容如下

from sqlalchemy.engine import create_engine
from sqlalchemy.ext.declarative.api import declarative_base
from sqlalchemy.orm.scoping import scoped_session
from sqlalchemy.orm.session import sessionmaker

engine = create_engine('sqlite:///fundmanager_devel.db', convert_unicode=True)
db_session = scoped_session(sessionmaker(autocommit=False,
                                         autoflush=False,
                                         bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()

【问题讨论】:

  • db_session 在哪里定义?你能添加代码吗?
  • 添加为问题的编辑

标签: python sqlalchemy flask-sqlalchemy


【解决方案1】:

似乎是会话混淆的问题,我按如下方式使用它并且它有效:

from <project> import db
from <project.models import Category

category = QuerySelectField('category', query_factory=lambda: db.session.query(Category), get_pk=lambda a: a.id, get_label=lambda a: a.name)

【讨论】:

  • 使用 db.session.query(Category) 而不是 Category.query.all() 为我工作。谢谢!
  • 使用 lambda query_factory=lambda: db.session.query(Country).filter_by(enabled=True).all() 代替 query_factory=db.session.query(Country).filter_by(enabled=True).all 对我有用。
【解决方案2】:

QuerySelectField 也有类似的问题:

forms.py:

class SurgeryForm(Form):
    study = QuerySelectField('Study',
                             query_factory=StudyGroup.query.all,
                             get_label='name')

模型.py

class Animal(db.Model):
    study_id = db.Column(db.Integer, db.ForeignKey('study_group.id'))

class StudyGroup(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    animals = db.relationship('Animal', backref='group', lazy='dynamic')

views.py:

def do_surgery():
    form = SurgeryForm(request.form)

    if form.validate_on_submit():
        a = models.Animal()
        form.populate_obj(a)  # Gather the easy stuff automagically
        #a.group = form.data['study']  #FAILS!
        a.study_id = form.data['study'].id  #WORKS!

似乎 SQLAlchemy(或者可能是 Flask-SQLAlchemy 或 Flask-WTF)使用一个会话来收集 QuerySelectField 中的值,并使用另一个会话来创建新的 Animal 对象。

如果您尝试使用 backref (Animal.group) 将 StudyGroup 对象附加到 Animal,您会遇到这个问题(因为对象与不同的会话相关联)。我使用的解决方法是直接设置外键(Animal.study_id)。

显然,我的回答有点晚了,但我希望它对某人有所帮助!

【讨论】:

    【解决方案3】:

    这很奇怪。如果您使用flask-sqlalchemy,为什么要显式创建引擎和声明性基础?这很可能是你的问题所在。看起来您有两个引擎和会话同时运行,这就是您收到错误的原因。

    不要显式创建引擎,您应该只使用:

    from flask.ext.sqlalchemy import SQLAlchemy
    
    db = SQLAlchemy()
    

    在您的应用工厂内部:

    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///fundmanager_devel.db'
    db.init_app(app) 
    

    那么你的基本声明模型是db.Model,你的会话在db.session,你应该让flask请求上下文管理会话创建。

    查看 Flask-SQLAlchemy 文档中的最小应用示例:

    http://pythonhosted.org/Flask-SQLAlchemy/quickstart.html#a-minimal-application

    这是 SQLAlchemy 推荐的方式:

    大多数 Web 框架都包含用于建立与请求相关联的单个 Session 的基础架构,该 Session 在请求结束时被正确构建和拆除,相应地拆除。此类基础设施部分包括 Flask-SQLAlchemy 等产品,用于与 Flask Web 框架结合使用,以及 Zope-SQLAlchemy,用于与 Pyramid 和 Zope 框架结合使用。 SQLAlchemy 强烈建议尽可能使用这些产品。

    http://docs.sqlalchemy.org/en/rel_0_9/orm/session.html

    【讨论】:

    • 我已经阅读了一些文档,这就是它应该被定义的方式。我有一个正在运行的应用程序具有这样的设置,它没有引起任何问题。我会听取您的建议并更改我的设置并报告
    • 这就是你在没有 Flask 的 SQLAlchemy 应用程序上使用它的方式。如果您使用 Flask 或其他框架,您应该使用提供的基础设施来保证只有一个会话。我编辑了我的答案以添加提及这一点的 SQLAlchemy 文档。
    • 这可能不是我的问题。我已将我的应用程序更改为不使用声明性,但问题仍然存在。我怀疑我的带有 db 初始化的 init 运行了两次(使用打印调试检查它)并创建了两次 db 连接。我找不到问题,我已经用教程完成了应用程序设置。这一定是非常愚蠢的事情,但我就是找不到。
    • 可以使用声明式。问题是您显式创建引擎和会话,而 flask-sqlalchemy 将隐式创建另一个。
    • 事实证明这根本不是会话/声明性问题。在我编写的代码中,我正在处理一个主要使用 QuerySelectFields 创建的表单。从该数据填充对象时一定有问题,因为在同一函数中添加和提交其他数据工作正常。
    猜你喜欢
    • 2018-06-08
    • 2018-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-26
    • 2020-08-06
    • 2014-07-08
    相关资源
    最近更新 更多