Auth in Django with other fields

I have a Django rest framework app and I modified its User model with AbstractBaseUser. I deployed the project on a VPS and now the customer wants to make changes on authentication. I have to set BOTH phone and email as USERNAME_FIELD to let users send phone or their email and I have to make it dynamic.

Here's my first approach:

from django.db import models
from django.core.validators import RegexValidator, MinValueValidator
from django.contrib.auth.models import  BaseUserManager, AbstractBaseUser, PermissionsMixin


class AllUser(BaseUserManager):
    def create_user(self, username, phone, email=None, password=None, first_name=None, last_name=None):
        if not email:
            raise ValueError('need Email')
        
        if not username:
            raise ValueError('need Email')
        
        if not phone:
            raise ValueError('need Phone Number')
        
        if not first_name:
            raise ValueError('First Name')
        
        if not last_name:
            raise ValueError('Last Name')

        user = self.model(
            email=self.normalize_email(email),
            phone=phone,
            username=username,
            first_name=first_name,
            last_name=last_name,
        )
        user.is_active = True
        user.set_password(password)
        user.save(using=self._db)
        return user

    def create_staff(self, username, phone, email, password, first_name, last_name):
        user = self.create_user(
            email=email,
            phone=phone,
            username=username,
            password=password,
            first_name=first_name,
            last_name=last_name,
        )
        user.is_active  = True
        user.is_staff = True
        user.is_superuser = False        
        user.save(using=self._db)
        return user

    def create_superuser(self, username, phone, email, password, first_name, last_name):
        user = self.create_user(
            email=email,
            phone=phone,
            username=username,
            password=password,
            first_name=first_name,
            last_name=last_name,
        )
        user.is_staff = True
        user.is_active  = True
        user.is_superuser = True
        user.save(using=self._db)
        return user


class User(AbstractBaseUser, PermissionsMixin):
    alphanumeric  = RegexValidator(r'^[0-9a-zA-Z]*$', message='ASCII Characters Only!')
    numbers       = RegexValidator(r'^[0-9a]*$', message='Numbers Only!')
    phone         = models.CharField(max_length=11, validators=[numbers], blank=True, null=True)
    email         = models.EmailField(max_length=244, blank=True, null=True)
    first_name    = models.CharField(max_length=30, null=True, blank=True)
    last_name     = models.CharField(max_length=50, null=True, blank=True)
    is_active     = models.BooleanField(default=True, null=False)
    is_staff      = models.BooleanField(default=False, null=False)
    is_superuser  = models.BooleanField(default=False, null=False)

    # TODO: Changeable between `phone` and `email`
    username      = models.CharField(max_length=244, unique=True)

    objects = AllUser()

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['phone', 'email', 'first_name', 'last_name']

    # TODO: Override `save` method to save user's USERNAME_FIELD 
    def save(self, *args, **kwargs):
        if self.phone:
            self.username = self.phone
        elif self.email:
            self.username = self.email
        super(User, self).save(*args, **kwargs)
    
    @property
    def fullName(self):
        return f"{self.first_name} {self.last_name}"

    def __str__(self):
        return f"{self.username}"
    
    def has_perm(self, perm, obj=None):
        "Does the user have a specific permission?"
        # Simplest possible answer: Yes, always
        return True

    def has_module_perms(self, app_label):
        "Does the user have permissions to view the app `app_label`?"
        # Simplest possible answer: Yes, always
        return True
    
    class Meta:
        # TODO : Representation of a single object from User model
        verbose_name = 'User'
        # TODO : Representation of User model in admin panel
        verbose_name_plural = 'Users'
        ordering = ['last_name']

The problem is I can't delete the database because its in production mode. And I have to change the USERNAME_FIELD as a dynamic choice. I couldn't run the migrate command and it gives me this error:

psycopg.errors.UniqueViolationError: Could not create unique index "user_user_username_key" 
DETAIL: Key (username)=['self']) is duplicated

Is there possible to do that while VPS is active? to make USERNAME_FIELD dynamic?

you need to have a validator which can include the phone no or email or username. createa function to validate that username is correct or not, and add it against username_validator field as below

below is basic example

def username_email_phone_no_validtor(username):
    # validation logic for email or phone no 
    return True # or false


class User(AbstractBaseUser, PermissionsMixin):
    :
    :
        username_validator = username_email_phone_no_validtor
    :
    : # other fields

this should allow you to add phone no or emial or anything for username which you want.

Back to Top