从 many-to-many SQL-Alchemy 和 Postgresql 中删除

Deleting from many-to-many SQL-Alchemy and Postgresql

我正在尝试从 sql-alchemy 中的 many-to-many 关系中删除 child object。

我不断收到以下错误:

StaleDataError: DELETE statement on table 'headings_locations' expected to delete 1 row(s); Only 2 were matched.

我查看了一些现有的 stackexchange 问题 (SQLAlchemy DELETE Error caused by having a both lazy-load AND a dynamic version of the same relationship, SQLAlchemy StaleDataError on deleting items inserted via ORM sqlalchemy.orm.exc.StaleDataError, , Delete from Many to Many Relationship in MySQL) 关于这个以及阅读文档,但无法弄清楚为什么它不起作用。

我定义关系的代码如下:

headings_locations = db.Table('headings_locations',
        db.Column('id', db.Integer, primary_key=True),
        db.Column('location_id', db.Integer(), db.ForeignKey('location.id')),
        db.Column('headings_id', db.Integer(), db.ForeignKey('headings.id')))


class Headings(db.Model):
    __tablename__ = "headings"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80))
    version = db.Column(db.Integer, default=1)
    special = db.Column(db.Boolean(), default=False)
    content = db.relationship('Content', backref=db.backref('heading'), cascade="all, delete-orphan") 
    created_date = db.Column(db.Date, default=datetime.datetime.utcnow())
    modified_date = db.Column(db.Date, default=datetime.datetime.utcnow(), onupdate=datetime.datetime.utcnow())

    def __init__(self, name):
        self.name = name



class Location(db.Model):

    __tablename__ = "location"
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(80), unique=True)
    account_id = db.Column(db.Integer, db.ForeignKey('account.id')) 
    version = db.Column(db.Integer, default=1)
    created_date = db.Column(db.Date, default=datetime.datetime.utcnow())
    modified_date = db.Column(db.Date, default=datetime.datetime.utcnow())
    location_prefix = db.Column(db.Integer)
    numbers = db.relationship('Numbers', backref=db.backref('location'), cascade="all, delete-orphan") 
    headings = db.relationship('Headings', secondary=headings_locations, 
                            backref=db.backref('locations', lazy='dynamic', cascade="all"))


    def __init__(self, name):
        self.name = name

而我的删除代码如下:

@content_blueprint.route('/delete_content/<int:location_id>/<int:heading_id>')
@login_required
def delete_content(location_id, heading_id):
    import pdb
    pdb.set_trace()
    location = db.session.query(Location).filter_by(id = location_id).first()
    heading = db.session.query(Headings).filter_by(id = heading_id).first()
    location.headings.remove(heading)
    #db.session.delete(heading)
    db.session.commit()
    flash('Data Updated, thank-you')
    return redirect(url_for('content.add_heading', location_id=location_id))

无论我尝试删除 child object(db.session.delete(标题)或 location.headings.remove(标题),我仍然遇到相同的错误。

非常感谢任何帮助。

我的数据库是postgresql。

编辑: 我添加关系的代码:

        new_heading = Headings(form.new_heading.data)
        db.session.add(new_heading)
        location.headings.append(new_heading)
        db.session.commit()

我认为错误消息是正确的:确实在您的数据库中有 2 行 link LocationHeading 实例。在这种情况下,您应该首先找出发生这种情况的地点和原因,并防止再次发生这种情况

  1. 首先,为了证实这个假设,您可以运行对您的数据库进行以下查询:

    q = session.query(
        headings_locations.c.location_id,
        headings_locations.c.heading_id,
        sa.func.count().label("# connections"),
    ).group_by(
        headings_locations.c.location_id,
        headings_locations.c.heading_id,
    ).having(
        sa.func.count() > 1
    )
    
  2. 假设,假设得到证实,通过手动删除数据库中的所有重复项(每个只保留一个)来修复它。

  3. 之后,添加一个UniqueConstraint到你的headings_locations table:

    headings_locations = db.Table('headings_locations',
            db.Column('id', db.Integer, primary_key=True),
            db.Column('location_id', db.Integer(), db.ForeignKey('location.id')),
            db.Column('headings_id', db.Integer(), db.ForeignKey('headings.id')),
            db.UniqueConstraint('location_id', 'headings_id', name='UC_location_id_headings_id'),
    )
    

注意需要添加到数据库中,添加到sqlalchemy模型中是不够的

现在错误插入重复项的代码将因违反唯一约束异常而失败,您可以解决问题的根源。