在子类unique_together元选项中使用基类字段时出错

时间:2010-10-05 18:51:23

标签: django django-models

使用以下代码:

class Organization(models.Model):
    name = models.CharField(max_length="100",)
    alias = models.SlugField()
    ...

class Division(Organization):
    parent_org = models.ForeignKey(Organization)

    class Meta:
        unique_together=['parent_org', 'alias']
        ...

尝试syncdb给我这个错误:

Error: One or more models did not validate:
organizations.division: "unique_together" refers to alias. This is not in the 
same model as the unique_together statement.

感谢任何帮助,

谢谢,

埃里克

5 个答案:

答案 0 :(得分:14)

这是设计的。阅读unique_together选项的文档,它指出:

  

它在Django管理员中使用,在数据库级别强制执行

如果查看子类创建的表,您将看到实际不具有其父级所具有的字段。相反,它使用名为[field]_ptr_id的字段名称获取父表的软外键,其中[field]是您从排除应用名称继承的表的名称。因此,您的分区表有一个名为organization_ptr_id的主外键。

现在因为unique_together使用UNIQUE约束在数据库级别强制执行,所以我不知道数据库是否实际将其应用于不在表中的字段。

您最好的选择可能是在业务逻辑层使用Validators,或重新考虑数据库架构以支持约束。

编辑:正如Manoj指出的那样,您也可以尝试使用validate_uniqueModel Validators

答案 1 :(得分:3)

[型号]验证器适合您。但也许最简单的方法是使用:

class BaseOrganization(models.Model):
    name = models.CharField(max_length="100",)
    alias = models.SlugField()
    class Meta:
        abstract = True

class Organization(BaseOrganization):
    pass

class Division(BaseOrganization):
    parent_org = models.ForeignKey(Organization)

    class Meta:
        unique_together=['parent_org', 'alias']

注意:与您当前的代码一样,您无法进行细分。

答案 2 :(得分:2)

这是我最近在Django 1.6中使用的解决方案(感谢Manoj Govindan的想法):

class Organization(models.Model):
    name = models.CharField(max_length="100",)
    alias = models.SlugField()
    ...

class Division(Organization):
    parent_org = models.ForeignKey(Organization)

    # override Model.validate_unique
    def validate_unique(self, exclude=None):     
        # these next 5 lines are directly from the Model.validate_unique source code
        unique_checks, date_checks = self._get_unique_checks(exclude=exclude)
        errors = self._perform_unique_checks(unique_checks)
        date_errors = self._perform_date_checks(date_checks)
        for k, v in date_errors.items():
            errors.setdefault(k, []).extend(v)

        # here I get a list of all pairs of parent_org, alias from the database (returned 
        # as a list of tuples) & check for a match, in which case you add a non-field 
        # error to the error list
        pairs = Division.objects.exclude(pk=self.pk).values_list('parent_org', 'alias')
        if (self.parent_org, self.alias) in pairs:
                errors.setdefault(NON_FIELD_ERRORS, []).append('parent_org and alias must be unique')

        # finally you raise the ValidationError that includes all validation errors, 
        # including your new unique constraint
        if errors:
            raise ValidationError(errors)

答案 3 :(得分:2)

这并不是严格适用于这个问题,而是非常密切相关的;如果基类是抽象的,unique_together将起作用。您可以使用以下方式标记抽象模型类:

class Meta():
    abstract = True

这将阻止django为类创建表,并且其字段将直接包含在任何子类中。在这种情况下,unique_together是可能的,因为所有字段都在同一个表中。

https://docs.djangoproject.com/en/1.5/topics/db/models/#abstract-base-classes

答案 4 :(得分:0)

当尝试将unique_together应用于给定客户端创建的权限组时,我遇到了类似的挑战。

class ClientGroup(Group):
    client = models.ForeignKey(Client, on_delete=models.CASCADE)
    is_active = models.BooleanField()

    class Meta():
        # looking at the db i found a field called group_ptr_id.
        # adding group_ptr did solve the problem.
        unique_together = ('group_ptr', 'client', 'is_active')