如果父行将在SQLAlchemy中被孤立,则禁止删除父行

时间:2017-03-23 13:53:41

标签: python postgresql flask sqlalchemy relationship

我目前正在使用sqlalchemy通过flask-sqlalchemy构建数据模型

数据库位于Postgresql服务器上

从具有关系的表中删除行时遇到问题。在这种情况下,我有许多治疗类型和一种治疗方法。治疗只有一种治疗类型。

只要我有一种或多种治疗分配了特定治疗类型,我希望不能删除治疗类型。现在它在我尝试时被删除了。

我有以下型号:

class treatment(db.Model):
    __tablename__ = 'treatment'
    __table_args__ = (db.UniqueConstraint('title', 'tenant_uuid'),)

    id = db.Column(db.Integer, primary_key=True)
    uuid = db.Column(db.String(), nullable=False, unique=True)
    title = db.Column(db.String(), nullable=False)
    tenant_uuid = db.Column(db.String(), nullable=False)

    treatmentType_id = db.Column(db.Integer, db.ForeignKey('treatmentType.id'))
    riskResponse_id = db.Column(db.Integer, db.ForeignKey('riskResponse.id'))

class treatmentType(db.Model):
    __tablename__ = 'treatmentType'
    __table_args__ = (db.UniqueConstraint('title', 'tenant_uuid'),)

    id = db.Column(db.Integer, primary_key=True)
    uuid = db.Column(db.String(), nullable=False, unique=True)
    title = db.Column(db.String(), nullable=False)
    tenant_uuid = db.Column(db.String(), nullable=False)

    treatments = db.relationship('treatment', backref='treatmentType', lazy='dynamic')

我可以在我的"删除"中构建一些逻辑在删除治疗类型之前检查指定治疗的视图,但在我看来,这应该是关系数据库的标准功能。换句话说,我一定是做错了。

我删除了这样的治疗类型:

entry = treatmentType.query.filter_by(tenant_uuid=session['tenant_uuid']).all()
    try:
        db.session.delete(entry)
        db.session.commit()
        return {'success': 'Treatment Type deleted'}
    except Exception as E:
        return {'error': unicode(E)}

正如我所说,我可以在删除治疗类型之前进行检查,但如果在删除之前存在关系问题,我宁愿让sqlalchemy抛出错误。

1 个答案:

答案 0 :(得分:7)

删除TreatmentType(父级)by default时,SQLAlchemy将通过设置Treatment.treatmentType_id = None来更新子级。如您所述,您只剩下Treatment而没有TreatmentType。儿童记录现在是一个“孤儿”。

有两种方法可以防止在SQLAlchemy中创建孤立记录。

<强> 1。在子列上使用非NULL约束

删除TreatmentType(父)时,默认情况下,SQLAlchemy会在执行此操作时将Treatment.treatmentType_id(子)设置为None(或在SQL中为null),并且你说过,你留下的Treatment没有TreatmentType

对此的解决方案是将treatmentType_id列更新为不可为空,这意味着它必须具有非空值。我们使用nullable=False关键字来执行此操作:

treatmentType_id = db.Column(db.Integer, db.ForeignKey('treatmentType.id'), nullable=False)

现在,当执行默认级联逻辑时,SQLAlchemy会尝试设置Treatment.treatmentType_id = None,并且由于违反了非空约束而引发IntegrityError

<强> 2。使用passive_deletes ='all'

treatments = db.relationship('treatment', backref='treatmentType', passive_deletes='all')

TreatmentType被删除时,passive_deletes='all'关系中的treatments关键字“will disable the “nulling out” of the child foreign keys”。它基本上禁用了上面第一段中概述的默认行为。因此,当ORM尝试删除TreatmentType 而不首先设置子项Treatment.treatmentType_id = None时,数据库将抛出IntegrityError抱怨孩子的ForeignKey引用非 - 存在的父母!

*注意:底层数据库必须支持外键才能使用此选项