2010-08-15 105 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) 
相关问题