1

私はフラスコとsqlalchemyをまったく使用していません。フラスコをよりよく理解するためだけに、1 つの Web サイトを作成しようとしています。ユーザーが投稿を作成して複数のタグを設定できるブログのようなものがあります。私はフラスコ sqlalchemy と wtforms-alchemy を使用しています。私は2つのモデルを持っています

association_table = db.Table('association',
    db.Column('post_id', db.Integer, db.ForeignKey('post.id')),
    db.Column('category_id', db.Integer, db.ForeignKey('category.id'))
)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(80), nullable=False)
    body = db.Column(db.Text, nullable=False)
    pub_date = db.Column(db.DateTime)

    categories = db.relationship('Category', secondary=association_table,
                            backref=db.backref('posts', lazy='dynamic'))

    ...
class Category(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50))
    ...

次のビューで新しい投稿を作成できます

def add():
    form = PostForm(request.form)
    if request.method == 'POST' and form.validate():
        post = Post(form.title.data, form.body.data)
        db.session.add(post)
        tags = form.tags.data.split(',')
        for tag in tags:
            category = Category.query.filter_by(name=tag.strip()).first()
            if not category:
                category = Category(tag.strip())
            post.categories.append(category)
        db.session.commit()
        return redirect(...)
    return render_template(...)

それは正常に機能します。しかし、投稿の編集にはいくつかの問題があります。タイトルまたは本文を変更するときは、db.session.merge(post) を使用する必要があります。そうしないと、データベースに変更を保存したくありません。しかし、すべてのチュートリアルでそのマージ方法はありません。2 つ目の問題は、投稿のタグを更新できないことです。新しいタグを作成して、投稿に追加できます。しかし、既存のタグを投稿に追加しようとすると、エラーが発生します。

AssertionError: A conflicting state is already present in the identity map for key (<class 'db.models.Category'>, (1,))

私の見解は次のように見えます

def edit(post_id):
    post = Post.query.get(post_id)
    form = PostForm(request.form, post)
    if request.method == 'POST' and form.validate():

        ...

        tags = form.tags.data.split(',')
        for tag in tags:
            category = Category.query.filter_by(name=tag.strip()).first()
            if not category:
                category = Category(tag.strip())
            db.session.remove()
            post.categories.append(category)
        form.populate_obj(post)
        db.session.merge(post)
        db.session.commit()
        return redirect(...)
    else:
        ...

    return render_template(...)

また、db.session.remove() を使用しないと、別のエラーが発生します

InvalidRequestError: Object '<Category at 0x7f9de40c0d90>' is already attached to session '6' (this is '1')

どこが間違っていますか?助けてくれてありがとう!

1 つの更新。テスト用です。1 つの投稿からタグのリストを繰り返し処理すると、それらのタグを削除して追加し直すことができます。

tags = list(post.categories)
for tag in tags:
    post.categories.remove(tag)

for tag in tags:
    post.categories.append(tag)    

しかし、タグを削除してからデータベースからタグを取得して追加しようとすると、エラーが発生しました

tags = list(post.categories)
for tag in tags:
    post.categories.remove(tag)

for tag in tags:
    category = Category.query.filter_by(name=tag.name).first()
    post.categories.append(tag) 


AssertionError: A conflicting state is already present in the identity map for key (<class 'db.models.Category'>, (1,)) 

私は混乱しています

4

2 に答える 2

3

どうして

db.session.remove()

あなたのコードで?

簡単な経験則sqlalchemy:

新しいインスタンスを作成する場合にのみ、それをセッションに追加する必要があります。クエリによって返されるもの (リレーションシップにも有効) はすべて、既にセッション内にあるため、何かを追加または削除する必要はありません。

したがって、新しいカテゴリを追加するには、単純に

  • オブジェクトを作成する
  • セッションに追加
  • categoriesリストに追加
  • db.session.commit

カテゴリを変更するには

  • categories目的のエントリを変更します
  • db.session.commit

関連付けを削除するには

  • categoriesリストから削除
  • db.session.commit

カテゴリ オブジェクトを削除するには、次のことも行う必要があります

  • db.session.delete(cat)
于 2013-08-26T08:23:20.800 に答える