背景: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()
答案 0 :(得分: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)
答案 1 :(得分:5)
我在QuerySelectField中遇到了类似的问题:
forms.py:
class SurgeryForm(Form):
study = QuerySelectField('Study',
query_factory=StudyGroup.query.all,
get_label='name')
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')
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)。
显然,我对这个答案有点迟到了,但我希望它有所帮助!
答案 2 :(得分:4)
这很奇怪。如果你使用flask-sqlalchemy,为什么要显式创建引擎和声明性基础?这可能是你的问题所在。看起来你有两个引擎和会话同时运行,这就是你得到错误的原因。
您应该只使用:
,而不是显式创建引擎from flask.ext.sqlalchemy import SQLAlchemy
db = SQLAlchemy()
在你的app工厂里面:
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///fundmanager_devel.db'
db.init_app(app)
然后您的基本声明模型为db.Model
,您的会话位于db.session
,您应该让flask请求上下文管理会话创建。
检查Flask-SQLAlchemy docs中的最小应用程序示例:
http://pythonhosted.org/Flask-SQLAlchemy/quickstart.html#a-minimal-application
这是SQLAlchemy推荐的方式:
大多数Web框架都包含用于建立与请求相关联的单个会话的基础结构,该会话在请求结束时被正确构造并拆除。这些基础设施包括Flask-SQLAlchemy等产品,与Flask Web框架结合使用,以及Zope-SQLAlchemy,与Pyramid和Zope框架结合使用。 SQLAlchemy强烈建议使用这些产品。