2010-08-15 44 views
7

我看到一些ppl在我面前有這個問題,但在舊版本的Django上,我在1.2.1上運行。Django unique_together不能與ForeignKey一起工作=無

我有一個模型,看起來像:

class Category(models.Model): 
objects = CategoryManager() 

name = models.CharField(max_length=30, blank=False, null=False) 
parent = models.ForeignKey('self', null=True, blank=True, help_text=_('The direct parent category.')) 

class Meta: 
    unique_together = ('name', 'parent') 

每當我試圖在管理類別保存設置爲無父,它仍然有效時,有具有相同的名稱和父集另一類到無。

想法如何優雅地解決這個問題?

回答

9

獨特的一起約束在數據庫級別實施,看來您的數據庫引擎不會將空值應用於約束。

在Django 1.2中,您可以爲您的模型定義一個clean method以提供自定義驗證。在你的情況下,你需要一些東西來檢查具有相同名稱的其他類別,只要父類爲None。

class Category(models.Model): 
    ... 
    def clean(self): 
     """ 
     Checks that we do not create multiple categories with 
     no parent and the same name. 
     """ 
     from django.core.exceptions import ValidationError 
     if self.parent and Category.objects.filter(name=self.name).exists(): 
      raise ValidationError("Another Category with name=%s and no parent already exists % self.name) 

如果您通過Django管理員編輯類別,則會自動調用clean方法。在您自己的觀點中,您必須致電category.fullclean()

+0

的一般方法看起來不錯,在這裏,但我'不遵循'如果self.parent和Category.objects.filter(name = self.name).exists():'的邏輯''這看起來像我正在檢查父母是否存在和另一個具有相同名稱的類別存在。這是我們想要的?如果self.parent == None和FolderUpload.objects.filter(name = self.name,parent = None).exists():'? – 2011-07-23 19:28:37

+0

我認爲你是對的。我會使用parent_id__is null = True而不是parent = None。它可能需要一個exclude()來忽略當前對象。 – Alasdair 2011-07-23 22:11:35

+0

我會離開一個星期,所以無法糾正答案。隨意編輯它,如果你想/可以。 – Alasdair 2011-07-23 22:12:45

5

我有這樣的問題太多,並通過與clean方法(如麥金太爾建議)創建一個超級名模解決了它,並把它作爲基類爲我所有型號:

class Base_model(models.Model): 
    class Meta: 
    abstract=True 

    def clean(self): 
    """ 
    Check for instances with null values in unique_together fields. 
    """ 
    from django.core.exceptions import ValidationError 

    super(Base_model, self).clean() 

    for field_tuple in self._meta.unique_together[:]: 
     unique_filter = {} 
     unique_fields = [] 
     null_found = False 
     for field_name in field_tuple: 
      field_value = getattr(self, field_name) 
      if getattr(self, field_name) is None: 
       unique_filter['%s__isnull'%field_name] = True 
       null_found = True 
      else: 
       unique_filter['%s'%field_name] = field_value 
       unique_fields.append(field_name) 
     if null_found: 
      unique_queryset = self.__class__.objects.filter(**unique_filter) 
      if self.pk: 
       unique_queryset = unique_queryset.exclude(pk=self.pk) 
      if unique_queryset.exists(): 
       msg = self.unique_error_message(self.__class__, tuple(unique_fields)) 
       raise ValidationError(msg) 
相關問題