Django删除异物?

时间:2010-02-14 23:07:33

标签: django

如果我们设置了Django如何推荐的配置文件:

class Profile(models.Model):
    user = models.ForeignKey(User, unique=True)

然后当您从Django admin中删除User对象时,它也会删除其配置文件。这是因为配置文件具有用户的外键,并且它希望保护参照完整性。但是,即使指针朝另一个方向发展,我也想要这个功能。例如,在我的Profile课程中,我有:

shipper = models.ForeignKey(Shipper, unique=True, blank=True, null=True)
carrier = models.ForeignKey(Carrier, unique=True, blank=True, null=True)
affiliat = models.ForeignKey(Affiliate, unique=True, blank=True, null=True, verbose_name='Affiliate')

我想要它,以便如果你删除Profile它将删除相关的托运人/承运人/联盟对象(不要问我为什么Django使“联盟”成为一些奇怪的关键词)。因为托运人,承运人和附属公司是用户类型,没有剩下的数据就没有意义存在(没有人可以作为一个人登录)。

我没有将密钥放在其他对象上的原因是因为每当我想检查用户的类型时,Django必须在内部加入所有这些表...

3 个答案:

答案 0 :(得分:11)

虽然使用上面bernardo描述的post_delete信号是一种很好的方法,但这种方法效果很好,我尽量避免使用尽可能少的信号,因为我觉得它通过向标准功能添加行为而不必要地使用代码人们可能期待的地方。

我更喜欢上面的压倒一切的方法,然而,Felix给出的例子确实有一个致命的缺陷;它覆盖的delete()函数如下所示:

def delete(self, using=None):
    using = using or router.db_for_write(self.__class__, instance=self)
    assert self._get_pk_val() is not None, "%s object can't be deleted because its %s attribute is set to None." % (self._meta.object_name, self._meta.pk.attname)

    collector = Collector(using=using)
    collector.collect([self])
    collector.delete()

注意参数'using',在大多数情况下我们用空参数调用delete(),所以我们甚至可能知道它在那里。在上面的示例中,此参数由我们覆盖而不是查看超类功能,如果有人在删除Profile时传递'using'参数将导致意外行为。为避免这种情况,我们会确保将参数与其默认lika一起保留:

class Profile(models.Model):
# ...

def delete(self, using=None):
    if self.shipper:
        self.shipper.delete()
    if self.carrier:
        self.carrier.delete()
    if self.affiliat:
        self.affiliat.delete()
    super(Profile, self).delete(using)

然而,最重要的方法的一个缺陷是,在批量删除时,每个数据库记录都不会显式调用delete(),这意味着如果您想要一次删除多个配置文件并保持覆盖行为(例如,在django查询集上调用.delete())你需要利用删除信号(如bernardo所述),否则你需要遍历每个记录单独删除它们(昂贵和丑陋)。

答案 1 :(得分:5)

在删除实际配置文件之前,您可以使用Profile类的override the delete()方法并删除此方法中的其他对象。

类似的东西:

class Profile(models.Model):
    # ...

    def delete(self):
        if self.shipper:
            self.shipper.delete()
        if self.carrier:
            self.carrier.delete()
        if self.affiliat:
            self.affiliat.delete()
        super(Profile, self).delete()

答案 2 :(得分:5)

更好的方法是使用对象的删除方法和查询集的删除方法,使用post_delete信号,如documentation中所示。

在您的情况下,您的代码与此非常相似:

from django.db import models
from django.dispatch import receiver

@receiver(models.signals.post_delete, sender=Profile)
def handle_deleted_profile(sender, instance, **kwargs):
    if instance.shipper:
        instance.shipper.delete()
    if instance.carrier:
        instance.carrier.delete()
    if instance.affiliat:
        instance.affiliat.delete()

这仅适用于Django 1.3或更高版本,因为此Django版本中添加了post_delete信号。