Hi guys, I'm new here.

I customised the User model of my project by inheriting from 
AbstractBaseUser, I've also made a custom Manager through BaseUserManager 
and added an auth backend to login through email instead of username.
The reason I did this is to let users register and login using their email 
but still have a username, that doesn't have to be unique (they're 
differentiated through a discriminator not unlike the one Discord uses, if 
you know it, like brzrkr#0001 and brzrkr#2342 may exist at the same time 
and both be known as brzrkr).

The problem is, when I create a user with `manage.py createsuperuser`, the 
database saves the email in the "username" field and the username in the 
"email" field, even though I set all overridden variables to the correct 
fields (see below for code).

NOTE: I have also posted a question about this on stackoverflow but it's 
been ignored for some days now, so I've looked for a place where people may 
be more familiar with Django in particular. Here is the link to 
it: https://stackoverflow.com/q/50104202/8599834

Now here's the *code of the User model* not including irrelevant methods:
class User(AbstractBaseUser, PermissionsMixin):
    """Override of the Django class to handle user authentication."""
    # ==Identification==
    # To allow for multiple users with the same username, users are 
publicly identified with username#identifier.
    # Internally, only the couple username + identifier must be unique, but 
either can be duplicate on their own.
    username = models.CharField(max_length=150, db_column="user_username")
    # The identifier is stored as a string of digits including trailing 
zeroes.
    # It is assigned at creation and modification of a user's nick using 
the first value that doesn't conflict.
    identifier = models.CharField(max_length=settings.IDENTIFIER_DIGITS)

    email = models.EmailField(unique=True, db_column="user_email")

    objects = UserManagerOverride()  # Override the default manager to 
customise user creation.

    is_superuser = models.BooleanField(default=False)
    is_staff = models.BooleanField(default=False)
    is_active = models.BooleanField(default=True)

    USERNAME_FIELD = "email"
    EMAIL_FIELD = "email"
    REQUIRED_FIELDS = ["username"]
    
        class Meta:
        unique_together = ("username", "identifier")

This is the *Manager*. This is mostly identical to what BaseUserManager 
does except for some quirks. I haven't included the generate_identifier 
method because it's not relevant:
class UserManagerOverride(BaseUserManager):
    """Override of the default Django user manager."""
    def _create_user(self, email, username, password, **extra_fields):
        """Override the parent function to create a user and save it in the 
database."""
        if not email:
            raise ValueError('An email is required.')

        # Prepare the data to be stored into the database.
        email = self.normalize_email(email)
        username = self.model.normalize_username(username)
        identifier = self.generate_identifier(username)

        # Make the user instance.
        user = self.model(username=username, email=email, 
identifier=identifier, **extra_fields)

        # Add the password after encrypting.
        user.set_password(password)

        try:
            # Save to database.
            user.save(using=self._db)
        except Exception:
            # If there was an error, scratch the user row to preserve 
integrity with User and UserData primary keys.
            user.delete()
            raise
        else:
            # Create a UserData record for this user.
            # Thanks to the try-else, we only do this if the code raised no 
exceptions.
            UserData.objects.create(user=user)

        return user

    def create_user(self, email, username, password=None, **extra_fields):
        """Override parent function to create a regular user account."""
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)
        return self._create_user(email, username, password, **extra_fields)

    def create_superuser(self, email, username, password, **extra_fields):
        """Override parent function to create a superuser account."""
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

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

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

And finally the *auth backend*, although this should only come into play 
when trying to log in, so it isn't involved in the problem (at least for 
now):
class EmailBackend(ModelBackend):
    """Allow authentication using email instead of username."""
    def authenticate(self, username=None, password=None, **kwargs):
        user_model = get_user_model()
        try:
            user = user_model.objects.get(email=username)
        except user_model.DoesNotExist:
            return None
        else:
            if user.check_password(password):
                return user
        return None

Please let me know if you need more info and thank you for reading this. I 
hope someone can walk me out of this conundrum... :P

-- 
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/django-users.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-users/ef29e0ef-9d60-49d3-8a65-5e80aee7691d%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to