如何清理 Django 孤儿外键值?
Posted
技术标签:
【中文标题】如何清理 Django 孤儿外键值?【英文标题】:How to Cleanup Django Orphan Foreign Key Values? 【发布时间】:2014-02-11 20:25:53 【问题描述】:我正在创建一个模型,该模型引用第 3 方包中的模型 -- Celery(CrontabSchedule 和 PeriodicTask)。我的模型(我们称之为 ScheduledRun)将包含一个 PeriodicTask 的外键。
我知道如果我删除外键本身会发生级联删除,并且引用该外键的父级也将被删除。 (除非被 on_delete 覆盖...)
但是由于我将 ScheduledRun 指向 PeriodicTask 的 FK 的情况,当我删除 ScheduledRun 时,PeriodicTask 不会被自动删除。 (也不应该因为可能有其他模型指向该外键!)
那么我如何清理孤立的 PeriodicTasks —— 即,当没有模型实例再指向它时。
我可以添加一个 post_delete 信号并以这种方式检查它(此示例正在删除与周期性任务无关的无关 CrontabSchedules:
# periodictask below is actually a related periodictask_set,
# but in Django you refer to the empty set as 'periodictask=None'
CrontabSchedule.objects.filter(id=instance.crontab.id,
periodictask=None).delete()
但我不能保证没有可能导致级联下降的其他相关关系。
我可以将表 PeriodicTask 子类化为 ScheduledRun .... 但不想将其与 3rd 方模型紧密集成。
这几乎就像我想要一个 .delete(do_not_cascade=True) 并且如果由于限制而失败,则忽略失败。如果它成功了,那么它就是一个孤儿。 on_delete=DO_NOTHING 与此类似,但我只希望暂时在单次删除的范围内使用,不想修改第三方包。
还有其他/更好的方法来处理这个问题吗?
【问题讨论】:
【参考方案1】:这是我的解决方案......似乎它可能足够强大。我的目标是仅在没有其他模型实例仍然引用它的情况下删除外键值。
所以,我将尝试基于每个相关键值为无的删除:
# This is a class method to my ScheduledRun class that has
# a foreign key to a PeriodicTask. PeriodicTask has a
# FK to a CrontabSchedule, which I'd like to "trim"
# if nothing points to that FK anymore.
@classmethod
def _post_delete(cls, instance, **kwargs):
instance.periodic_task.delete()
# Delete the crontab if no other tasks point to it.
# This could cause a cascade if we don't check all related...
filter = dict(id=instance.crontab.id)
for related in instance.crontab._meta.get_all_related_objects():
filter[related.var_name] = None
assert('id' in filter)
assert('schedule' in filter)
assert('periodictask' in filter)
CrontabSchedule.objects.filter(**filter).delete()
如果我能说会更容易:
instance.crontab.delete(NO_CASCADE=True)
【讨论】:
以上是关于如何清理 Django 孤儿外键值?的主要内容,如果未能解决你的问题,请参考以下文章
如何在Django中查询具有特定数量的外键关系并且在这些外键值中具有特定值的对象?