Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 593
alias51 Avatar asked Apr 15 '26 06:04

alias51


1 Answers

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.

like image 175
Willem Van Onsem Avatar answered Apr 16 '26 18:04

Willem Van Onsem