【问题标题】:Flask/WTF/SQLAlchemy: using QuerySelectMultipleField with Form.populate_obj()Flask/WTF/SQLAlchemy:使用 QuerySelectMultipleField 和 Form.populate_obj()
【发布时间】:2016-03-09 13:30:06
【问题描述】:

我有一个包含 QuerySelectMultipleField 的表单(WTForms via Flask-WTF),如下所示:

class EditDocumentForm(Form):
    # other fields omitted for brevity
    users = QuerySelectMultipleField('Select Users',
                             query_factory=User.query.all,
                             get_label=lambda u: u.username)

这很好用——我只是实例化表单并将它传递给我的模板进行渲染,所有正确的选择都在那里。

但是,当我 POST 表单并尝试使用 Form.populate_obj() 提取数据时,我收到了来自 SQLAlchemy 的愤怒消息:

InvalidRequestError: Object '<User at 0x10a4d33d0>' is already attached to session '1' (this is '3')

视图函数:

@app.route("/document/edit/<doc_id>", methods=['GET', 'POST'])
@login_required
def edit_document(doc_id):
    doc = Document.query.filter_by(id=doc_id).first()
    if (doc is not None) and (doc.user_id == current_user.id):
        form = EditDocumentForm(obj=doc)
        if request.method == "POST":
            if form.validate():
                form.populate_obj(doc)
                db.session.commit()
                return redirect('/')
            else:
                _flash_validation_errors(form)
        return render_template("edit.html", form=form)
    flash("The document you requested doesn't exist, or you don't have permission to access it.", "error")
    return(redirect('/'))

所以看起来在创建表单时使用了一个会话,而当我尝试填充我的模型对象时使用了另一个会话。这一切都发生在幕后,因为我依靠 Flask-SQLAlchemy 为我做所有的会话工作。

Document 模型中,user 字段是这样声明的:

users = db.relationship('User',
                       secondary=shares,
                       backref=db.backref('shared_docs', lazy='dynamic'))

(当然sharesSQLAlchemy.table 的一个实例,用于多对多关系)。

那么:是我做错了什么,还是Form.populate_obj() 有问题,或者我可以责怪外星人?让我换个说法:我做错了什么?

编辑

解决方法this answer 似乎解决了这个问题,即通过导入我的 SQLAlchemy 对象并显式使用其会话来更改我的query_factory

query_factory=lambda: db.session.query(User)

不过,我不得不说,这对我来说有一种奇怪的气味。这真的是最好的处理方式吗?

【问题讨论】:

    标签: python flask flask-sqlalchemy flask-wtforms


    【解决方案1】:

    这完全取决于您的模型类如何绑定到会话。我的猜测是:您没有使用 Flask-SQLAlchemy 提供的基类:db.Model 用于您的 DocumentUser 模型?

    如您的“编辑”中所述,不使用Userquery 方法,而是使用db.session.query,您将强制populate_obj 使用您稍后将提交的同一会话您的db.session.commit 电话。也就是说,您在执行 Document.query.filter_by 时可能仍在使用另一个会话,这很可能意味着您仍在使用 2 个数据库连接,并且可以将其减少到一个。

    总的来说,我建议你不要在你的模型上使用 query 方法(但那是因为我不喜欢魔法的东西;)),如果可以的话,请确保使用 Flask-SQLAlchemy 的 db.Model并深入阅读您使用的框架/库是如何工作的,因为这是一个非常好的习惯,不会花费很多时间,并且可以显着提高代码的质量和可维护性。

    【讨论】:

    • 感谢您的回复。我的模型直接继承了 db.Model。从资源的角度来看,我并不担心多个连接,但我肯定希望有干净的代码。
    • hum .... 您确定在您的应用代码和模型之间使用相同的db 对象(从共享模块导入?)?如果它是共享的,它绝对应该使用相同的会话。
    猜你喜欢
    • 2015-04-07
    • 2014-04-07
    • 1970-01-01
    • 2011-04-20
    • 1970-01-01
    • 1970-01-01
    • 2016-04-22
    • 2017-08-17
    • 2015-03-25
    相关资源
    最近更新 更多