2010-02-14 40 views
15

如果我們建立了一個輪廓Django是如何建議:Django刪除異物?

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

然後,當你刪除Django管理的User對象,它會刪除自己的個人資料too.This是因爲輪廓具有外鍵,用戶就是了保護參照完整性。但是,即使指針是以其他方式運行,我也需要此功能。例如,在我的Profile I類有:

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的就必須在內部加入所有這些表每次我要檢查用戶是哪種類型的?時間

+0

「子公司」在Django中絕對不是「某種奇怪的關鍵字」。我可以創建一個名爲「affiliate」的字段並在我的代碼中使用它。 – 2010-02-15 15:12:02

+0

這很奇怪。一切正常,但它拒絕出現在管理部分。你檢查了嗎?像一週前的SVN結賬。 – mpen 2010-02-16 02:07:43

回答

10

雖然使用上面bernardo描述的post_delete信號是一種好的方法,但是這樣做會很好,我儘量避免使用盡可能少的人爲信號,因爲我覺得它通過向標準功能添加行爲來不必要地卷積代碼在人們可能期待的地方。

我更喜歡上面的重寫方法,但是,Felix給出的例子確實有一個致命的缺陷;刪除()函數,這是壓倒一切看起來像這樣:

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

通知參數「使用」,在大多數情況下,我們稱之爲空參數刪除(),所以我們甚至可能已經知道它在那裏。在上面的例子中,這個參數被我們覆蓋,而不是看超類的功能,如果有人在刪除Profile時傳遞'using'參數,會導致意外的行爲。爲了避免這種情況,我們會確保其默認利卡所以沿着保留的說法:

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) 

一個缺陷,以壓倒一切的方針,但是,是刪除()沒有得到明確每個數據庫記錄呼籲散裝刪除,這意味着如果你想要一次刪除多個配置文件並保持重載行爲(例如在django查詢集上調用.delete()),則需要使用刪除信號(如bernardo )或者你將需要遍歷每個記錄單獨刪除它們(昂貴和醜陋)。

+0

沒有必要指定爲什麼你回答的答案是好的,並且你也得到了好評 – dashesy 2015-04-18 00:04:04

+0

請注意,刪除( )方法在使用QuerySet批量刪除對象時不一定會調用。爲確保自定義的刪除邏輯得到執行,您可以使用pre_delete和/或post_delete信號。 https://docs.djangoproject.com/en/1.9/topics/db/models/#overriding-model-methods – dnaranjo 2016-02-11 12:57:43

+0

@dnaranjo是的,這是所有在我的答案結尾列出的陷阱佈局。儘管如此,感謝您的評論,希望沒有通讀完整答案的人通過閱讀您的意見可以發現潛在的隱患,並有助於爲他們的個人需求做出有教育的決定。 – krayzk 2016-02-24 16:42:02

5

你可以override the delete() Profile類的方法,並在刪除實際配置文件之前刪除此方法中的其他對象。

喜歡的東西:

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() 
+0

看起來不錯,但是當我通過Django管理員刪除東西時,似乎從未調用過delete()方法......? – mpen 2010-02-14 23:48:08

+2

@Mark:在刪除單個對象時,它至少應該可以工作。刪除多個對象似乎有一個問題:http://code.djangoproject.com/ticket/10751 – 2010-02-14 23:55:59

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信號。