Brittle unique_for

unique_for_date/month/year is very brittle and over time will likely be a source for unexpected behaviour.

unique_for_date/month/year prevents entry of two records with the same value on the same date or month or year.

However, this feature is unfortunately brittle:

  • This constraint is not enforced by the database.
  • It is checked during Model.validate_unique() and so will not occur if Model.save() is called without first calling Model.validate_unique().
  • The constraint will not be checked even if Model.validate_unique() is called when using a ModelForm that does not include a field involved in the check.
  • Only the date portion of the field will be considered, even when validating a DateTimeField.

Most of these problems can be mitigated by moving the validation to Model.save().

So in practice, do this


class SomeModel(models.Model):
    date = models.DateField()
    text = models.CharField()

    def save(self, *args, **kwargs):
        if self.objects.filter(date=self.date, text=self.text).exists():
            raise ValidationError({'text': 'Must be unique for date.'})
        return super().save(*args, **kwargs)

Instead of this


class SomeModel(models.Model):
    date = models.DateField()
    text = models.CharField(unique_for_date='date')

Are you affected? Check with
pip install django-doctor
.

Configuring this check

Django Doctor will run this check by default. No configuration is needed but the check can be turned on/off using check code brittle-unique-for in your pyproject.toml file.

Read more about configuring Django Doctor.