Flask中的(pymysql.err.IntegrityError)(1451,'无法删除或更新父行:外键约束失败...')

时间:2018-03-06 07:20:19

标签: python flask sqlalchemy

我知道标题中有很多关于错误的问题,但我找不到合适的解决方案。我的问题是,在使用Session.delete()删除行时,它正在抛出

  

sqlalchemy.exc.IntegrityError: (pymysql.err.IntegrityError) (1451, 'Cannot delete or update a parent row: a foreign key constraint fails (`transport`.`driver`, CONSTRAINT `driver_ibfk_1` FOREIGN KEY (`owner_id`) REFERENCES `truckcompany` (`id`))') [SQL: 'DELETE FROM truckcompany WHERE truckcompany.id = %(id)s'] [parameters: {'id': 4}]

型号:

class Truck_company(Base):

    __tablename__ = 'truckcompany'

    id = Column(BigInteger, primary_key=True)

class Driver(Base):

    __tablename__ = 'driver'

    id = Column(BigInteger, primary_key=True)
    owner_id = Column(BigInteger, ForeignKey('truckcompany.id'))
    owner = relationship(Truck_company)

删除失败的视图:

@app.route('/values/deleteuser/<int:id>', methods=['POST', 'GET'])
def delete_truck(id):
    value_truckcompany = sqlsession.query(Truck_company).filter(Truck_company.id == id).first()
    if value_truckcompany:
        sqlsession.delete(value_truckcompany)
        sqlsession.commit()
        return redirect('/static/truckcompanyview', )

1 个答案:

答案 0 :(得分:2)

为什么

Driver模型中,有一个引用Truck_company的外键约束:

class Driver(Base):
    ...
    owner_id = Column(BigInteger, ForeignKey('truckcompany.id'))

您已省略ON DELETE操作,因此MySQL defaults to RESTRICT。此外,没有SQLAlchemy ORM与级联的关系会删除相关的驱动程序。因此,当您尝试在视图中删除卡车公司时,数据库会阻止您执行此操作,因为您违反了外键约束,换句话说referential integrity。这是你如何建模数据库的问题,而不是Flask等。

我能做什么

最重要的事情 - 当你创建你的模型时 - 是决定当你删除一个有相关驱动程序的卡车公司时你想要发生什么。您的选择包括但不限于:

  1. 删除驱动程序。
  2. owner_id设置为NULL,有效地将其分离。如果在父级的默认配置中存在ORM关系,那么这就是SQLAlchemy所做的。
  3. 这也是一个完全有效的解决方案,可以限制删除带子节点的父行,正如您已经隐式完成的那样。

    您已在评论中表示您要删除相关的驱动程序。快速解决方案是手动发出DELETE:

    # WARNING: Allowing GET in a data modifying view is a terrible idea.
    # Prepare yourself for when Googlebot, some other spider, or an overly
    # eager browser nukes your DB.
    @app.route('/values/deleteuser/<int:id>', methods=['POST', 'GET'])
    def delete_truck(id):
        value_truckcompany = sqlsession.query(Truck_company).get(id)
    
        if value_truckcompany:
            sqlsession.query(Driver).\
                filter_by(owner=value_truckcompany).\
                delete(synchronize_session=False)
            sqlsession.delete(value_truckcompany)
            sqlsession.commit()
            return redirect('/static/truckcompanyview', )
    

    另一方面,这仅修复了这一个位置。如果您确定Driver没有Truck_company没有意义,则可以alter the foreign key constraint包含ON DELETE CASCADE,并在相关的SQLAlchemy ORM关系中使用passive deletes

    class Truck_company(Base):
        ...
        # Remember to use passive deletes with ON DELETE CASCADE
        drivers = relationship('Driver', passive_deletes=True)
    
    class Driver(Base):
        ...
        # Let the DB handle deleting related rows
        owner_id = Column(BigInteger, ForeignKey('truckcompany.id',
                                                 ondelete='CASCADE'))
    

    或者你可以将它留给SQLAlchemy ORM级别级联来删除相关对象,但似乎你在过去遇到了一些问题。请注意,SQLAlchemy cascades定义operation on the parent应如何传播给其子级,因此您在方关系中定义delete和可选delete-orphan,或者一对多的一面:

    class Truck_company(Base):
        ...
        # If a truck company is deleted, delete the related drivers as well
        drivers = relationship('Driver', cascade='save-update, merge, delete')
    

    在您当前的模型中,您从Truck_companyDriver定义了关系,因此不会发生级联。

    请注意修改Driver,例如:

    class Driver(Base):
        ...
        owner_id = Column(BigInteger, ForeignKey('truckcompany.id',
                                                 ondelete='CASCADE'))
    

    神奇地迁移现有的数据库表及其约束。如果您希望采用该路线,则必须migrate manually或使用某种工具。