Felix6
Felix6

Reputation: 23

How to make fields unique with other models referencing the same ForeignKey

Is there a way to make model fields unique with other models referencing the same ForeignKey?

In my case I am trying to make it so that the names of Attribute models with the same Obj are unique. Ex: if there exists a CharAttribute with name='Types' and obj=1 there can't be another CharAttribute with name='Types' and obj=1 nor an IntAttribute with name='Types' and obj=1

class Obj (models.Model):
    name = models.CharField(max_length=40)


class CharAttribute (models.Model):
    name = models.CharField(max_length=40)
    obj = models.ForeignKey(Obj, on_delete=models.CASCADE)


class IntAttribute (models.Model):
    name = models.CharField(max_length=40)
    obj = models.ForeignKey(Obj, on_delete=models.CASCADE)

My current idea for a solution would be something like this:

def clean_fields(self, exclude=None):
    super().clean_fields(exclude=exclude)
    for other_name in IntAttribute.objects.filter(obj=self.obj):
        if self.name == other_name:
            raise ValidationError(
                ('Attributes of the same object may not have the same name, %(name) s already exists for '
                 '%(object) s' % {'name': self.name, 'object': self.obj.name})
            )

    for other_name in CharAttribute.objects.filter(obj=self.obj):
        if self.name == other_name:
            raise ValidationError(
                ('Attributes of the same object may not have the same name, %(name) s already exists for '
                 '%(object) s' % {'name': self.name, "object": self.obj.name})
            )

But creating another for loop for each possible future created Attribute feels repetitive and there must be a better way of accomplishing this task.

Thank you in advance for the feedback.

Upvotes: 0

Views: 36

Answers (1)

p14z
p14z

Reputation: 1810

You can use unique_together to make sure no combination of fields is repeated in a model:

class CharAttribute (models.Model):
    name = models.CharField(max_length=40)
    obj = models.ForeignKey(Obj, on_delete=models.CASCADE)

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

Between models you would have to do a manual validation:

class CharAttribute (models.Model):
    name = models.CharField(max_length=40)
    obj = models.ForeignKey(Obj, on_delete=models.CASCADE)

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

    def clean(self):
        if IntAttribute.objects.filter(name=self.name, obj=self.obj).exists():
            raise ValidationError('...')

The same method applies for the other model.

Upvotes: 1

Related Questions