如何清理Django Orphan外键值?

时间:2014-01-22 00:14:33

标签: python django postgresql celery

我正在创建一个引用第三方包中的模型的模型--Celery(CrontabSchedule和PeriodicTask)。我的模型(我们称之为ScheduledRun)将包含PeriodicTask的外键。

我知道如果删除外键本身会发生级联删除,并且引用该外键的父级也会被删除。 (除非被on_delete覆盖...)

但由于我将PeriodledRun指向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 ....但是不希望与第三方模型紧密集成。

这几乎就像我想要一个.delete(do_not_cascade = True),如果由于约束而失败,只需忽略失败。如果它成功了,那就是一个孤儿。 on_delete = DO_NOTHING与此类似,但我只希望它只在暂时中用于单个删除的范围,而且我不想修改第三方包。

还有其他/更好的方法可以解决这个问题吗?

1 个答案:

答案 0 :(得分:0)

这是我的解决方案......看起来它可能足够强大。我的目标是只删除外键值,如果没有其他模型实例仍然引用它。

所以,我将尝试删除基于每个相关键值为None:

# 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)