【问题标题】:(pymysql.err.IntegrityError) (1451, 'Cannot delete or update a parent row: a foreign key constraint fails...') in Flask(pymysql.err.IntegrityError)(1451,'无法删除或更新父行:外键约束失败......')在Flask
【发布时间】:2018-08-13 23:42:19
【问题描述】:

我知道有很多关于标题错误的问题,但我找不到合适的解决方案。我的问题是,当使用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', )

【问题讨论】:

  • 我想你还有一张表是指卡车公司表?
  • 可以举个例子。
  • 仔细阅读错误driver 中有一行引用了 truckcompany 中您要删除的行。您必须考虑在这种情况下您希望发生什么。还要删除驱动程序吗?或者将他们的owner_id 设置为 NULL?和往常一样,提供minimal reproducible example。例如,您遗漏了您的驱动程序模型。通常,如果您包含您正在使用的数据库,它也会有所帮助,尽管在这种情况下,从错误消息中可以明显看出。
  • 我刚刚更新了 orm.py,如果你现在帮我,先生。
  • 至于为什么会发生这种情况:"For an ON DELETE or ON UPDATE that is not specified, the default action is always RESTRICT. " 要么更改 DB 外键约束以使用 ON DELETE CASCADE 并修改您的 SQLA 模型以反映这一点,手动删除与 Truck_company 相关的 Drivers要删除,或使用 SQLA 级联(您已经尝试过)。

标签: python flask sqlalchemy


【解决方案1】:

为什么

在您的 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 所做的。

这也是一个非常有效的解决方案,可以限制删除带有子行的父行,正如您隐式所做的那样。

您已在 cmets 中表示要删除相关驱动程序。一个快速的解决方案是手动发出 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', )

另一方面,这仅修复了这个位置。如果您认为没有DriverDriver 没有它的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 应如何传播到其子代,因此您可以在 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要么使用一些工具。

【讨论】:

  • 一切正常。但是,删除记录一两个。之后显示 sqlalchemy.exc.IntegrityError: (pymysql.err.IntegrityError) (1451, '无法删除或更新父行:外键约束失败 (transport.driverallocation,约束driverallocation_ibfk_1外键(driver_id)引用driverid))')[SQL:'从驱动程序中删除驱动程序.id =%(id)s'][参数:{'id': 2}]
  • 它允许一到两条记录。
  • 那个错误看起来很眼熟不是吗?这又是同一件事:表 driverallocation 中的外键限制您删除驱动程序。请注意,您最初并未在帖子中包含该表。只需经历与以前相同的思考过程即可。当驱动程序被删除(prolly delete 以及)时,驱动程序分配会发生什么。
  • 只是我试图删除第一行。但是,它没有删除,而剩下的行正在删除,为什么?
  • 再说一次,我没有你的表格,也没有你的数据。我不知道。请停止使用单一问答作为通用支持服务。
猜你喜欢
  • 1970-01-01
  • 2018-05-21
  • 1970-01-01
  • 1970-01-01
  • 2022-09-23
  • 2020-08-10
  • 2017-06-11
  • 1970-01-01
相关资源
最近更新 更多