无法在Flask中更新具有多对多关系的条目

时间:2013-08-25 21:49:07

标签: python flask wtforms

我对flask和sqlalchemy全新。我尝试建立一个网站只是为了更好地了解烧瓶。我有博客,用户可以创建帖子和设置多个标签。我使用flask-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),否则它不想保存数据库中的更改。但是在所有教程中都没有合并方法。 第二个问题是我无法更新帖子的标签。我可以创建一个新标签并将其添加到某个帖子中。但是,如果我尝试将现有标签添加到任何帖子我都有错误。

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')

我哪里错了?谢谢你的帮助!

一次更新。只是为了测试。如果我从一个帖子迭代标签列表,我可以删除这些标签并将其追加。

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

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

但是如果我删除标签然后从db获取标签并尝试追加 - 得到错误

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,)) 

我很困惑

2 个答案:

答案 0 :(得分:3)

为什么

db.session.remove()
你的代码中有

吗?

sqlalchemy的快速经验法则:

只有在创建新实例时,才必须将其添加到会话中。 查询返回的所有内容(对关系也有效)已经在会话中,因此您无需添加或删除任何内容。

所以要添加一个新类别,你只需

  • 创建对象
  • 添加到会话
  • 附加到categories列表
  • db.session.commit

修改类别

  • 修改所需的categories条目
  • db.session.commit

删除关联

  • categories列表中删除
  • db.session.commit

要删除类别对象,您还需要

  • db.session.delete(cat)

答案 1 :(得分:1)

我发现了问题。我使用WooshAlchemy进行文本搜索。它会创建不同的会话。关于这一点的大文章可以找到here