Why Django unique constraints did not work?

Viewed 55

I've added UniqueConstraint into the constraints array in the Meta class which is a subclass of User table.

class User(AbstractBaseUser):
...
    class Meta:
         verbose_name = _("account")
        verbose_name_plural = _("accounts")
        indexes = [
            models.Index(name="email_index", fields=["email"])
        ]
        constraints = [
            models.CheckConstraint(name="check_birth_date", check=Q(birth_date__lt=f"{datetime.date.today()}")),
            models.CheckConstraint(name="check_phone_number", check=Q(phone_number__regex=r"^09(1[0-9]|2[0-9]|3[0-9])[0-9]{3}[0-9]{4}$")),
            models.UniqueConstraint(name="username_email_unique", fields=["username", "email"])
        ]

My expectation was that prevent user to enter username and email with same value, But actually did not work and user can do it without getting the IntegrityError Moreover, I'm using Postgresql

1 Answers

My expectation was that prevent user to enter username and email with same value.

No: if you specify two or more fields, it means that the combination of these fields should be unique. In other words if there is a user A and a user B, it will only reject creating B if both the .username and the .email are the same.

You need to make the two fields individually unique. You can do that with the unique=… parameter [Django-doc]:

class User(AbstractBaseUser):
    username = models.CharField(max_length=255, unique=True)
    email = models.EmailField(unique=True)
    # …

Or if you want to do that through the constraints, you make two UniqueConstraints, each for one field:

class User(AbstractBaseUser):
    # …
    class Meta:
        # …
        constraints = [
            models.CheckConstraint(name="check_birth_date", check=Q(birth_date__lt=f"{datetime.date.today()}")),
            models.CheckConstraint(name="check_phone_number", check=Q(phone_number__regex=r"^09(1[0-9]|2[0-9]|3[0-9])[0-9]{3}[0-9]{4}$")),
            models.UniqueConstraint(name="username_unique", fields=["username"]),
            models.UniqueConstraint(name="email_unique", fields=["email"])
        ]

If you want to prevent that you enter the same value for both the username and the email field for each record, you need to add an extra CheckConstraint constraint [Django-doc]:

from django.db.models import F, Q

class User(AbstractBaseUser):
    # …
    class Meta:
        # …
        constraints = [
            # …
            models.CheckConstraint(check=~Q(username=F('email')), name='username_email_unique')
        ]
Related