[Solved]-Django – Allow duplicate usernames

10👍

I’m not sure if this is exactly what you’re looking for, but I think you could use a hack similar to what is in this answer.

The following code works, as long as it is in a place that gets executed when Django loads your models.

from django.contrib.auth.models import User

User._meta.get_field('username')._unique = False

Note that this won’t change the database unique constraint on the auth_user table if it has been already been created. Therefore you need to make this change before you run syncdb. Alternatively, if you don’t want to recreate your auth_user table, you could make this change and then manually alter the database table to remove the constraint.

7👍

What you can do is extend the User model. For the User table, generate a username (e.g. A_123, A_345) that will not be displayed at all in the site.

Then create a new model that extends User.

class AppUser(User):
    username = models.CharField...
    organization = models.CharField...

You then create a custom authentication backend that use AppUser instead of the User object.

1👍

I have not personally been required to find a solution to this, but one way to tackle this (from an SAAS perspective) would be to prefix the username with an organizational identifier (presuming unique organizations). For example: subdomain.yoursite.com would equate to a user with the username: subdomain_username. You would just have to code some business logic on login to a subdomain to tack that onto the username.

1👍

I also suffered with this problem. I was doing a project where I had to use email and mobile no. as login fields but none of them should be unique because their were different types of users and a user can have more than one user entity and also the project required only one auth user table (Hectic right!).

So I extended AbstractBaseUser class where I could change the USERNAME_FIELD attribute. Here’s how :-

from django.contrib.auth.models import AbstractUser
from django.contrib.auth.models import PermissionsMixin

# Custom model for User
class User(AbstractBaseUser, PermissionsMixin):

    first_name = models.CharField(max_length=100, blank=False)
    last_name = models.CharField(max_length=100, blank=True)
    password = models.CharField(max_length=255, blank=False)
    email = models.EmailField(max_length=255, blank=False)
    mobile = models.CharField(max_length=12)
    user_type = models.ForeignKey(UserTypes, on_delete=models.DO_NOTHING)
    is_approved = models.BooleanField(default=False)

    objects = UserManager()
    # Here's the Catch
    USERNAME_FIELD = 'id'

    def get_full_name(self):
        '''
        Returns the first_name plus the last_name, with a space in between.
        '''
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        '''
        Returns the short name for the user.
        '''
        return self.first_name

    def email_user(self, subject, message, from_email=None, **kwargs):
        '''
        Sends an email to this User.
        '''
        send_mail(subject, message, from_email, [self.email], **kwargs)

    class Meta:
        db_table = 'user'

Yes exactly, surprised? USERNAME_FIELD should be a unique field that’s the constraint of this attribute. I couldn’t use email or mobile no. as unique field.

Then I created a custom manager to remove the username field (reference = https://simpleisbetterthancomplex.com/tutorial/2016/07/22/how-to-extend-django-user-model.html#abstractbaseuser)

from django.contrib.auth.base_user import BaseUserManager


class UserManager(BaseUserManager):
    use_in_migrations = True

    def _create_user(self, email, password, **extra_fields):
        """
        Creates and saves a User with the given email and password.
        """
        if not email:
            raise ValueError('The given email must be set')
        email = self.normalize_email(email)
        user = self.model(email=email, **extra_fields)
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_user(self, email, password=None, **extra_fields):
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, password, **extra_fields)

    def create_superuser(self, email, password, **extra_fields):
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, password, **extra_fields)

This will do the trick.

0👍

I’m facing the exact same problem and I’ve been reading a lot (about how to solve that problem in 1.5) and I just thought of a much simpler solution. What if you just add a fixed-length prefix with the organization id to store the username?

I.e. Organization id = 115, chosen username = “john” and a fixed length of 6. So in the data base you store as username “000115_john”.

When you do the login you just join the two parameters and try to authenticate with what Django provides. I’m not sure if the fixed length is strictly necessary but could avoid undesirable results if a user chooses a username with only numbers.

Leave a comment