alias51
alias51

Reputation: 8638

How to test uniqueness in a Model.clean() function?

I have a model with a UniqueConstraint:

class MyModel(models.Model)
    name = models.CharField()
    title = models.CharField()
  
    class Meta:
        constraints = [ models.UniqueConstraint(
                          fields=['name', 'title'],
                          name="unique_name_and_title") ]

This works fine and raises an IntegrityError when 2 objects with the same values are created.

The problem is UniqueConstraint doesn't present a pretty ValidationError to the user. Usually, I would add these in the Model.clean() class, but if I do this then it will fail on an Update because the instance being updated will already be present:

def clean(self):
    if MyModel.objects.filter(title=self.title, name=self.name):
             raise ValidationError({'title':'An object with this name+title already exists'})

I How do I create a ValidationError that passes if it's an UPDATE not an INSERT?

I know I could do this on a ModelForm and use self.instance to check if the instance already exists, but I want to apply this to the Model class and not have to rely on a ModelForm.

Upvotes: 3

Views: 92

Answers (1)

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477190

You can exclude the object from the queryset you check:

def clean(self):
    qs = MyModel.objects.exclude(pk=self.pk).filter(title=self.title, name=self.name)
    if qs.exists():
        raise ValidationError({'title':'An object with this name+title already exists'})
    return super().clean()

If the object is not yet saved, it will check for .exclude(pk=None), but that will not exclude any objects, since the primary key is non-nullable.

It is more efficient to use .exists() [Django-doc] here, since it limits the bandwidth from the database to the Django/Python layer.

Upvotes: 2

Related Questions