6

背景: 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' です)

add を merge に変更すると問題は解決しますが、次の点に注意してください。

  • 開始したばかりのオブジェクトをマージする必要がある理由がわかりません
  • 変更を加えてマージし、プロパティの 1 つを定義しようとすると、

    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()
4

3 に答える 3

5

QuerySelectField でも同様の問題がありました。

フォーム.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')

ビュー.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) を直接設定することです。

明らかに、私はこの答えでパーティーに少し遅れていますが、誰かの役に立てば幸いです!

于 2014-04-11T17:09:38.177 に答える
4

それは変だ。フラスコ sqlalchemy を使用している場合、エンジンと宣言ベースを明示的に作成するのはなぜですか? それがおそらくあなたの問題です。2 つのエンジンとセッションが同時に実行されているように見えるため、エラーが発生しました。

エンジンを明示的に作成する代わりに、次のものを使用する必要があります。

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-SQLAlchemy docs で最小限のアプリケーションの例を確認してください。

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

SQLAlchemy で推奨されている方法は次のとおりです。

ほとんどの Web フレームワークには、リクエストに関連付けられた単一のセッションを確立するためのインフラストラクチャが含まれています。このセッションは正しく構築され、リクエストの最後に対応する解体されます。このようなインフラストラクチャには、Flask Web フレームワークと組み合わせて使用​​する Flask-SQLAlchemy や、Pyramid および Zope フレームワークと組み合わせて使用​​する Zope-SQLAlchemy などの製品が含まれます。SQLAlchemy は、これらの製品を利用可能なものとして使用することを強くお勧めします。

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

于 2013-11-04T15:30:12.800 に答える