Aziz Alfoudari
Aziz Alfoudari

Reputation: 5263

Django - Removing username from user model

In order to create a new user model in Django 1.5.x, there are two approaches:

  1. Inherit AbstractUser class which is the default user model you get, this way you can extend it with any attributes you want. However, if you want to remove any field, it's technically possible but not recommended; even if it can be done, it is against OOP principles, I believe. So if you would like to alter the current user model, there is the second approach.
  2. Inherit AbstractBaseUser, which by looking at the code provides very basic functionality. You will miss all the goodness of permissions, profile retrieval and absolute url construction, unless you copy it from the default Django user model.

The above is my understanding of the situation. Correct me if I'm wrong, but doesn't this mean that if I want to simply remove the username field out of the model since I won't need it at all, I have to copy paste the model code provided in the framework and inherit from AbstractBaseUser and PermissionsMixin? For such a simple thing, this approach doesn't look very pretty to me, and it looks a bit odd since I'm quite certain the custom user model was introduced largely because of the popular use case of email field as the user identifier instead of username.

Your thoughts (and corrections) please.

Upvotes: 7

Views: 9066

Answers (3)

Rida Zouga
Rida Zouga

Reputation: 81

I know that this question is quite old now, but for anyone looking for a quick answer, I may have found an easy hack. Since I want to delete the username column only and keep all other fields (and add a few more), first we need to choose that unique identifier who will stand in place of username for authentication which in most cases like mine is simply the email field, now all we have to do is update the User model like this for example:

class User(AbstractUser):
    email = models.EmailField(unique=True)
    # Please note that the 3 following fields are extra which you don't have to add in your case
    phone_number = models.CharField(max_length=12, unique=True)
    last_logout = models.DateTimeField(null=True, blank=True)
    current_status = models.CharField(max_length=40)
    # This is necessary for making email field the identifier used for authentication
    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []

    # this is the hack for overriding username and thus isn't a column in the database
    @property
    def username(self):
        return self.get_username()
        # or maybe return self.get_full_name() or any other method

and just like that you implemented your custom User model and deleted the username field, kept all other fields and added a few more of your choice without the pain of using the AbstractBaseUser along with PermissionsMixin and starting all over again. Hope it helps.


Edit: Actually the base manager should be updated too unfortunately :(( which is definitely painful since create_user and create_superuser both require a positional argument of username, so make sure to copy this final version of code in your models.py file:

from django.contrib.auth.models import AbstractUser, BaseUserManager
from django.db import models


class CustomUserManager(BaseUserManager):
    def create_user(self, email, password=None, **extra_fields):
        if not email:
            raise ValueError("The Email field 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_superuser(self, email, password=None, **extra_fields):
        extra_fields.setdefault("is_staff", True)
        extra_fields.setdefault("is_superuser", True)
        return self.create_user(email, password, **extra_fields)


class User(AbstractUser):
    email = models.EmailField(unique=True)
    phone_number = models.CharField(max_length=12, unique=True)
    last_logout = models.DateTimeField(null=True, blank=True)
    current_status = models.CharField(max_length=40)
    USERNAME_FIELD = "email"
    REQUIRED_FIELDS = []

    objects = CustomUserManager()

    @property
    def username(self):
        return self.get_username()

PS: Of course don't forget to point to your new custom User model in your settings.py file using AUTH_USER_MODEL which you can read about here

Upvotes: 0

nithin h r
nithin h r

Reputation: 5

Rather than using mixins and other applications which solve the problem, The best way to tackle the problem of not wanting a username is to replace the username with the email address entered by the user.

What u need to do is, go to the django source code which is usually situated in the python path. usually in the site-packeges of the python folder,

go to django folder, and into contrib folder, and into auth folder, open the models.py file which contains the definition of the User model.

On Virtualenv -> <virtualenv_name>/lib/python2.7/site-packages/django

In the models.py of auth app

Go to -> AbstractUser class replace the regex in "validators.RegexValidator" with this:

r"^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$"

replace only the regex part inside the "validators.RegexValidator"

and in the same class, alter the username field's max_length to 254

I didnt have the reputation to post image of the code, if anybody wants a screenshot of how it looks after altering the code, ping me.

Thats it!

now go back to your app and then you can replace the username field with the email of any type in this world.

The final step : Go to your application where you are using the User model, meaning anywhere

python manage.py syncdb

python manage.py makemigrations

python manage.py migrate

if you don't see the auth model migrating then do,

python manage.py migrate auth

Now all you need to do is just replace the username with the email, during registration you can do somewhat like:

user = User.objects.create_user('email here', 'again email here', 'password here')

The best part about this is you dont have to change your authentication backend, as the email is itself the username and you can just label the username field as email.

Upvotes: -14

HankMoody
HankMoody

Reputation: 3174

If You look at the source code of the django.contrib.auth.models file then you will see that definition of the AbstractUser class is rather short and starts like this:

class AbstractUser(AbstractBaseUser, PermissionsMixin):
    ...

It inherits from the AbstractBaseUser and PermissionMixin. You could define your custom model and also inherit it from the mentioned classes to get permissions support. If you want all other model fields then yes, you will need to copy them, but it's also an opportunity to customize things to match your needs.

Upvotes: 5

Related Questions