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

Hen*_*sen 8 python postgresql sqlalchemy relationship flask

我目前正在使用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')
Run Code Online (Sandbox Code Playgroud)

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

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

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)}
Run Code Online (Sandbox Code Playgroud)

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

The*_*inn 10

删除TreatmentType(父)时,默认情况下,SQLAlchemy将通过设置更新子项Treatment.treatmentType_id = None.正如你所说的那样,你只剩Treatment下一个TreatmentType.儿童记录现在是一个"孤儿".

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

1.在子列上使用非NULL约束

删除TreatmentType(父)时,默认情况下,SQLAlchemy会在执行此操作时将Treatment.treatmentType_id(子)设置为None(或在SQL中为null),如您所述,您将保留一个Treatment不带a TreatmentType.

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

treatmentType_id = db.Column(db.Integer, db.ForeignKey('treatmentType.id'), nullable=False)
Run Code Online (Sandbox Code Playgroud)

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

2.使用passive_deletes ='all'

treatments = db.relationship('treatment', backref='treatmentType', passive_deletes='all')
Run Code Online (Sandbox Code Playgroud)

当a TreatmentType被删除时,关系passive_deletes='all'上的关键字treatments" 将禁用子外键 " 的"清空 ".它基本上禁用了上面第一段中概述的默认行为.因此,当ORM尝试删除而TreatmentType 没有先设置子项时Treatment.treatmentType_id = None,数据库将抛出一个IntegrityError,抱怨子项的ForeignKey引用了一个不存在的父项!

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