SQLAlchemy会话错误

Paw*_*ski 6 python sqlalchemy flask-sqlalchemy

背景: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()
Run Code Online (Sandbox Code Playgroud)

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

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()
    
    Run Code Online (Sandbox Code Playgroud)

    我刚刚在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()
Run Code Online (Sandbox Code Playgroud)

Ped*_*eck 5

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

您不应显式创建引擎,而应仅使用:

from flask.ext.sqlalchemy import SQLAlchemy

db = SQLAlchemy()
Run Code Online (Sandbox Code Playgroud)

在您的应用程序工厂内:

app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///fundmanager_devel.db'
db.init_app(app) 
Run Code Online (Sandbox Code Playgroud)

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

检查 Flask-SQLAlchemy 文档中的最小应用程序示例:

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

SQLAlchemy 是这样推荐的:

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

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


2ni*_*2ni 5

会话混淆似乎有问题,我使用它如下,它的工作原理:

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)
Run Code Online (Sandbox Code Playgroud)


小智 5

我在QuerySelectField中遇到了类似的问题:

forms.py:

class SurgeryForm(Form):
    study = QuerySelectField('Study',
                             query_factory=StudyGroup.query.all,
                             get_label='name')
Run Code Online (Sandbox Code Playgroud)

models.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')
Run Code Online (Sandbox Code Playgroud)

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!
Run Code Online (Sandbox Code Playgroud)

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

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

很明显,我对这个答案有点晚了,但我希望它可以帮到某个人!