【问题标题】:Deleting many-to-many: Flask-Sqlalchemy tries to delete every row with specific id删除多对多:Flask-Sqlalchemy 尝试删除具有特定 id 的每一行
【发布时间】:2018-02-07 00:25:51
【问题描述】:

我一直坚持使用烧瓶和烧瓶-sqlalchemy 从 MySQL 数据库中删除多对多记录。 好像 sqlalchemy 试图从关系表中删除所有行,其中 a 的 id 与链接到 b 的相同。

错误(示例):

sqlalchemy.orm.exc.StaleDataError: DELETE statement on table 'ksiazka_autor' expected to delete 1 row(s); Only 0 were matched.

sqlalchemy.orm.exc.StaleDataError: DELETE statement on table 'ksiazka_wydawnictwo' expected to delete 552 row(s); Only 551 were matched.

INFO sqlalchemy.engine.base.Engine DELETE FROM ksiazka_kategoria WHERE ksiazka_kategoria.ksiazka_id = %s AND ksiazka_kategoria.kategoria_id = %s
2017-08-29 15:44:34,965 INFO sqlalchemy.engine.base.Engine ((166, 40), (167, 40), (168, 40), (169, 40), (170, 40), (171, 40), (172, 40), (173, 40)  ... displaying 10 of 178 total bound parameter sets ...  (1463, 19), (2046, 19))

关系表:

ksiazka_autor = db.Table('ksiazka_autor',
        db.Column('autor_id', db.Integer, db.ForeignKey('autor.id')),
        db.Column('ksiazka_id', db.Integer, db.ForeignKey('ksiazka.id'))
    )
        ksiazka_wydawnictwo = db.Table('ksiazka_wydawnictwo',
        db.Column('wydawnictwo_id', db.Integer, db.ForeignKey('wydawnictwo.id')),
        db.Column('ksiazka_id', db.Integer, db.ForeignKey('ksiazka.id'))
    )
    ksiazka_ilustrator = db.Table('ksiazka_ilustrator',
        db.Column('ilustrator_id', db.Integer, db.ForeignKey('ilustrator.id')),
        db.Column('ksiazka_id', db.Integer, db.ForeignKey('ksiazka.id'))
    )
    ksiazka_kategoria = db.Table('ksiazka_kategoria',
        db.Column('ksiazka_id', db.Integer, db.ForeignKey('ksiazka.id')),
        db.Column('kategoria_id', db.Integer, db.ForeignKey('kategoria.id'))
    )

和模型:

class Book(db.Model):
    __tablename__ = 'ksiazka'
    id = db.Column('id', db.Integer, primary_key=True)
    [...]

    authors = db.relationship('Author', secondary=ksiazka_autor, backref='authors', cascade="all", lazy='dynamic')
    category = db.relationship('Category', secondary=ksiazka_kategoria, backref='category', cascade="all", lazy='dynamic')
    publisher = db.relationship('Publisher', secondary=ksiazka_wydawnictwo, backref='publisher', cascade="all", lazy='dynamic')
    artist = db.relationship('Artist', secondary=ksiazka_ilustrator, backref='artist', cascade="all", lazy='dynamic')

class Author(db.Model):
    __tablename__ = 'autor'
    id = db.Column('id', db.Integer, primary_key=True)
    [...]
    books = db.relationship('Book', secondary=ksiazka_autor, backref=db.backref('books'), lazy='joined')

class Category(db.Model):
    __tablename__ = 'kategoria'
    id = db.Column('id', db.Integer, primary_key=True)
    [...]
    cat_books = db.relationship('Book', secondary=ksiazka_kategoria, backref=db.backref('cat_books'), lazy='joined')

class Publisher(db.Model):
    __tablename__ = 'wydawnictwo'
    id = db.Column('id', db.Integer, primary_key=True)
    [...]
    pub_books = db.relationship('Book', secondary=ksiazka_wydawnictwo, backref=db.backref('pub_books'), lazy='joined')

class Artist(db.Model):
    __tablename__ = 'ilustrator'
    id = db.Column('id', db.Integer, primary_key=True)
    [...]
    art_books = db.relationship('Book', secondary=ksiazka_ilustrator, backref=db.backref('art_books'), lazy='joined')

最后是烧瓶代码:

@app.route('/delete/book/<id>')
def delete_book(id):
    book = Book.query.filter_by(id=id).first()
    db.session.delete(book)
    db.session.commit()
    return redirect('/home')

【问题讨论】:

  • 我认为这是由于cascade option 你在那里,请参考文档并明智地选择删除操作的不同行为。 docs.sqlalchemy.org/en/latest/orm/cascades.html
  • 嗯,最初我没有“ cascade="all" ”元素,我得到了同样的错误。

标签: python flask sqlalchemy many-to-many flask-sqlalchemy


【解决方案1】:

这是因为您已为所需的每个关系设置了两个单独的关系。您有一个名为Book.authors 的关系和一个名为Author.authors 的反向引用,以及一个名为Author.books 的关系和一个名为Book.books 的反向引用。这不起作用,因为它们都在ksiazka_autor 上运行,从而导致您看到的冲突。您的所有其他关系也是如此。

您想要的是使用back_populates 选项:

class Book(db.Model):
    ...

    authors = db.relationship('Author', secondary=ksiazka_autor, back_populates='books', cascade="all", lazy='dynamic')

class Author(db.Model):
    ...
    books = db.relationship('Book', secondary=ksiazka_autor, back_populates='authors', lazy='joined')

手动删除相关实例并提交两次不是正确的解决方案。

【讨论】:

  • 感谢您的洞察力!我明天试试。我还是 SQLAlchemy 的新手。
  • 这个答案拯救了我的一天!但是,我仍然需要更好地理解 'lazy' 属性在这种情况下的作用。
  • @AlmirCampos lazy 选项直接从问题中复制而来。如果您不需要lazy 选项(甚至不知道它是什么),您可以放心地忽略它。 lazy 选项的文档可在 docs.sqlalchemy.org/en/13/orm/loading_relationships.html 获得
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-05-03
  • 1970-01-01
  • 2021-11-23
  • 2020-04-12
  • 1970-01-01
  • 2010-10-18
  • 1970-01-01
相关资源
最近更新 更多