首页 > 解决方案 > Django - 外键约束的唯一集合

问题描述

我有两个这样的模型:

class Group(...):
    pass

class Identifier(...):
    value = models.CharField(...)
    group = models.ForeignKey('Group', ..., related_named = 'identifiers')

我怎样才能:

  1. 限制 aGroup最多只有 4 个Identifiers
  2. 确保最多 4 个Identifiers(标识符的值)的任意组合在所有Groups?

Groups对于第 2 部分,以下是展平表的示例:

row | id__0__val | id__1__val | id__2__val | id__3__val 
--- | ---------- | ---------- | ---------- | ----------
  0 |       abc  |        123 |        xyz |        456
  1 |       abc  |        123 |        xyz |          -   <-- valid (nulls are okay)
  2 |       123  |        abc |        xyz |        456   <-- invalid (same combo as row 0)   

以前我尝试过(类似的)这个,但它看起来很乱,功能有限,我不确定它是否会起作用:

class Group(...):
    id__0 = models.OneToOneField('Identifier', blank = True, null = True, ...)
    id__1 = models.OneToOneField('Identifier', blank = True, null = True, ...)
    id__2 = models.OneToOneField('Identifier', blank = True, null = True, ...)
    id__3 = models.OneToOneField('Identifier', blank = True, null = True, ...)

    class Meta: 
        unique_together = ('id__0__value', 'id__1__value', 'id__2__value', 'id__3__value')

处理这种约束的更好方法是什么?

标签: pythondjangodjango-models

解决方案


可以通过validate_unique方法做到这一点:

class Group(models.Model):
    ...

    def validate_unique(self, exclude=None):
        qs = Identifier.objects.filter(group_id=self.id)
        # restrict a Group to only have at most 4 Identifiers
        if qs.count() >= 4:
            raise ValidationError("group already has 4 Identifiers")
        # Ensure that any combination of up to 4 Identifiers is unique across all Groups
        values = []
        for group in Group.objects.all():
            values.append([i.value for i in group.identifiers])
        current_values = [i.value for i in self.identifiers]
        # https://stackoverflow.com/questions/22483730/python-check-if-list-of-lists-of-lists-contains-a-specific-list
        if current_values in values:
            raise ValidationError("group with these identifiers already exists")

        

这是伪代码,所以可能不起作用 - 但你明白了 :)


推荐阅读