【问题标题】:Trouble dropping rows involving inherited models and relationships with Flask-SQLAlchemy使用 Flask-SQLAlchemy 删除涉及继承模型和关系的行时遇到问题
【发布时间】:2016-04-29 19:09:30
【问题描述】:

我(糟糕地)为自己设计了一个涉及许多关系和继承模型的数据库,如下所示:

class Instrument(db.Model):
    __tablename__ = 'instrument'

    id = db.Column(db.Integer, primary_key = True)
    sn = db.Column(db.String(24), unique = True, index = True)
    ...
    data            = db.relationship('Data', backref = 'Instrument', lazy = 'dynamic', cascade = 'all, delete')
    sensors         = db.relationship('Sensor', backref = 'Instrument', lazy = 'dynamic', cascade = 'all, delete')


class Sensor(db.Model):
    __tablename__ = 'sensors'

    id = db.Column(db.Integer, primary_key = True)
    sn = db.Column(db.String(24), unique = True, index = True)
    ...
    data = db.relationship('SensorData', backref = 'Sensor', lazy = 'dynamic', cascade = 'all, delete')
    instr_sn = db.Column(db.String(24), db.ForeignKey('instrument.sn'), index = True)

class SensorTypeB(Sensor):
    __tablename__ = 'sensor_type_b'

    id      = db.Column(db.Integer, db.ForeignKey('sensors.id'), primary_key = True)
    extracolumn = db.Column(db.Float)

    __mapper_args__ = {'polymorphic_identity': 'sensor_type_b'}

    def __init__(self, extracolumn = None, **kwargs):
        super(SensorTypeB, self).__init__(**kwargs)
        self.extracolumn = extracolumn

class Data(db.Model):
    __tablename__ = 'data'

    id  = db.Column(db.Integer, primary_key = True)
    timestamp   = db.Column(db.DateTime)
    value       = db.Column(db.Float)
    parameter   = db.Column(db.String(24), index = True)
    unit        = db.Column(db.String(24))
    flag        = db.Column(db.Boolean)
    instr_sn    = db.Column(db.String(24), db.ForeignKey('instrument.sn'), index = True)


class SensorData(Data):
    __tablename__ = 'sensor_data'

    id  = db.Column(db.Integer, db.ForeignKey('data.id'), primary_key = True)
    sensor_sn = db.Column(db.String(24), db.ForeignKey('sensors.sn'), index = True)

    __mapper_args__ = {'polymorphic_identity': 'sensor_data'}

    def __init__(self, sensor_sn, **kwargs):
        super(SensorData, self).__init__(**kwargs)

        self.sensor_sn  = sensor_sn

class MetSensorData(SensorData):
    __tablename__ = 'met_sensor_data'

    id  = db.Column(db.Integer, db.ForeignKey('sensor_data.id'), primary_key = True)
    raw = db.Column(db.Float)

    __mapper_args__ = {'polymorphic_identity': 'met_sensor_data'}

    def __init__(self, raw = None, **kwargs):
        super(MetSensorData, self).__init__(**kwargs)

        self.raw = raw

为了简洁起见,我遗漏了一大块......但可以添加任何可能相关的细节。以这种方式设置它的目的(在我看来)是为了做到以下几点:

  • 每个传感器都必须属于一个仪器,但不是每个仪器都必须有一个传感器
  • 所有仪器都有数据 (Instrument.data)
  • 所有传感器都有数据 (Sensor.data)
  • 传感器模型的子类有数据 (SensorTypeB.data)

在我尝试从数据库中删除数据点之前,一切都按预期进行。它在使用 SQLite3 进行单元测试时工作得非常好,但是一旦我将它移到 MySQL,一切都会因类型错误而中断:

IntegrityError: (_mysql_exceptions.IntegrityError) (1451, 'Cannot delete or update a parent row: a foreign key constraint fails (`db_name`.`met
_sensor_data`, CONSTRAINT `met_sensor_data_ibfk_1` FOREIGN KEY (`id`) REFERENCES `sensor_data` (`id`))') [SQL: u'DELETE FROM sensor_data WHE
RE sensor_data.id = %s'] [parameters: (3L,)]

为什么这只发生在 MySQL 而不是 SQLite?我该如何修复和/或改进它?这不是有史以来最糟糕的数据库设计吗?

【问题讨论】:

  • SQLite 默认不强制使用外键。
  • 对。那么如何强制 MySQL 在删除时不注意 FK,或者以一种跨数据库不存在此问题的方式设计我的架构?

标签: python mysql flask sqlalchemy flask-sqlalchemy


【解决方案1】:

您收到的错误是告诉您您正在尝试删除 SensorData 行,但这样做时,MetSensorData 中引用它的条目将被孤立,并且由于 SQLAlchemy 配置SensorDataMetSensorData 之间的关系,MetSensorData 中的孤儿是不允许的。

如果您能找到不需要该类的方法,或者使其成为独立模型而不是 SensorData 模型的扩展,那么您的问题就会消失。如果您想保持模型原样,那么您可以在SensorData 外键中添加一个级联子句,指示数据库删除孤立行而不是抱怨它。我尚未对其进行测试以确保这是正确的语法,但我认为您可以按以下方式进行:

id  = db.Column(db.Integer, db.ForeignKey('sensor_data.id', ondelete='CASCADE'), primary_key = True)

【讨论】:

  • 太棒了!谢谢您的帮助。正如我在上面所做的那样,在 FK 上定义级联与在关系中定义级联有何不同?
  • 我不确定,但我的猜测是您的解决方案失败了,因为它应用于 Python 空间,而不是数据库空间。如果数据库没有强制执行 PK,那么我认为 SQLAlchemy 会在适当的时候清理掉那个孤立的行,但是由于这是分两步完成的,所以在步骤 #1 之后,数据库处于 MySQL 没有的不一致状态不喜欢。基于 SQL 的级联确保删除由数据库完成。
猜你喜欢
  • 2017-04-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-17
  • 1970-01-01
  • 1970-01-01
  • 2020-10-02
  • 2022-01-12
相关资源
最近更新 更多