【问题标题】:Prevent deletion of parent row if it's child will be orphaned in SQLAlchemy如果父行的子行将在 SQLAlchemy 中成为孤儿,则防止删除父行
【发布时间】:2017-08-16 03:04:27
【问题描述】:

我目前正在通过 flask-sqlalchemy 使用 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 抛出错误。

【问题讨论】:

    标签: python postgresql flask sqlalchemy relationship


    【解决方案1】:

    删除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 被删除时,treatments 关系“will disable the “nulling out” of the child foreign keys”上的 passive_deletes='all' 关键字。它基本上禁用了上面第一段中概述的默认行为。所以,当ORM试图删除TreatmentType没有首先设置孩子的Treatment.treatmentType_id = None时,数据库会抛出一个IntegrityError抱怨孩子的ForeignKey引用了一个不存在的父母!

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

    【讨论】:

    • 谢谢 Aelfinn 在这种情况下,这是要走的路。我目前有很多类似的。但是当相关项是可选的时候怎么办?
    • 答案已更新为 `passive_deletes='all'' 关键字 - 这应该可以为您提供所需的内容。
    猜你喜欢
    • 1970-01-01
    • 2017-09-27
    • 1970-01-01
    • 2011-10-12
    • 2018-04-15
    • 2010-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多