UniqueConstraint on USERNAME_FIELD

I am trying to set up a custom user model using email as the USERNAME_FIELD. I want to have the ability to have multiple users with the same email as long as only one of them is not soft deleted.

I though that using UniqueConstraint would achieve this, however I have tested it by creating users and soft deleting them and then attempting to create a new user with the same email. However, the user is not created. Also there is no exception that seems to be raised.

Any help would be appreciated, I’m not sure if my understanding of UniqueConstraint is wrong and it was not meant to do what I am attempting to do, or that because email is being used as the USERNAME_FIELD this creates additional complications.

models.py

class User(AbstractUser):
    username = None
    email = models.EmailField(unique=True)
    is_deleted = models.BooleanField(default=False)

    USERNAME_FIELD ="email"
    REQUIRED_FIELDS = []

    objects = UserManager()

    def __str__(self):
        return str(self.email)
    
    class Meta:
        constraints =[
            models.UniqueConstraint(
                fields=["email"],
                condition=Q(is_deleted=False),
                name="unique_active_email"
            )
        ]

forms.py

class UserCreateForm(UserCreationForm):
    class Meta:
        model = User
        fields = ("email", "password1", "password2")

views.py

def register(request):
    if request.method == "POST":
        form = UserCreateForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect("register")

    context = {"form": UserCreateForm}
    return render(request, 'register.html', context)

This is a really bad idea.

The short answer is that you can pretty much forget about using the Django authentication and authorization code if you try to do this.

Trying to do this is going to cause problems in multiple other locations in the authentication system. You’ll need to rewrite all the authentication-related middleware and large chunks of the authentication system. (“Large” in terms of the number of functions and classes affected, not necessarily in terms of the numbers of lines of code.)

Now, aside from that and regardless of what constraints you are looking to create, this:

is going to require that email be unique throughout the table. See the docs for unique.

1 Like

Thank you for the answering.

Would a better idea be to add null=True to the email field. And set soft deleted user to None, while also backing up the email on a terminated_email field. Or would that be a bad idea as well?

Well, that would at least avoid creating any problems that I can think of regarding the Django authentication and authorization systems.

So no, I don’t forsee any problems with that.

Side note - rather than changing the email field to null, you could do something like add a unique suffix to the email address, like the pk. (e.g., ken@example.com might become ken@example.com@152.) That way it’s guaranteed unique, and you have a simple mechanism to recover the original name without moving it between fields.
That also avoids allowing nulls in that field.

1 Like