Kurtis Nusbaum
Kurtis Nusbaum

Reputation: 30825

Django conditional unique together

I have a model that looks like the following:

class LibraryEntry(models.Model):
  host_lib_song_id = models.IntegerField()
  song = models.CharField(max_length=200)
  artist = models.CharField(max_length=200)
  album = models.CharField(max_length=200)
  owning_user = models.ForeignKey(User)
  is_deleted = models.BooleanField(default=False)

Now, if I so a select where is_deleted=False, the combination of host_lib_song_id and owning_user should be unique. How can I express this?

Upvotes: 8

Views: 7192

Answers (3)

Vaghinak
Vaghinak

Reputation: 595

You can use UniqueConstraint if you're using Django 2.2 +. You can see the full answer here.

Upvotes: 2

Chris Pratt
Chris Pratt

Reputation: 239350

Overriding validate_unique to check the uniqueness if is_deleted is False is more appropriate:

...

def validate_unique(self, exclude=None):
    if not self.is_deleted and \
       LibraryEntry.objects.exclude(pk=self.pk).filter(host_lib_song_id=self.host_lib_song_id, owning_user=self.owning_user).exists():
        raise ValidationError('Some error message about uniqueness required')
    super(LibraryEntry, self).validate_unique(exclude=exclude)

Upvotes: 12

Bernhard Vallant
Bernhard Vallant

Reputation: 50796

You cannot express this through the Meta.unique_together constraint, but through django's model validation:

class LibraryEntry(models.Model):
    def clean(self):
        from django.core.exceptions import ValidationError
        try:
            # try to find a duplicate entry and exclude 'self'
            duplicate = LibraryEntry.objects.exclude(pk=self.pk)\
                .get(owning_user=self.owning_user, 
                     host_lib_song_id=self.host_lib_song_id,
                     is_deleted=False)
            raise ValidationError('Library Entry already exists!')
        except: LibraryEntry.DoesNotExist:
            # no duplicate found
            pass

Upvotes: 4

Related Questions