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().

If we spot this issue in your GitHub pull request we give this advice:

models.pymaintainabilitymedium
class ExampleModel(models.Model):
    date = models.DateField()
-
    text = models.CharField(unique_for_date='date')
+
    text = models.CharField()
+
+
    def save(self, *args, **kwargs):
+
        # more verbose, but less brittle than unique_for_date
+
        if self.objects.filter(date=self.date, text=self.text).exists():
+
            raise ValidationError({'text': 'Must be unique for date.'})
+
        return super().save(*args, **kwargs)

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

Read more
Protect your pull requests from over 40 types of common Django technical debts with our GitHub code review bot.

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.