Django - Removing username from user model
Asked Answered
D

3

7

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.

Dextrocular answered 12/9, 2013 at 16:34 Comment(3)
This is not recommended in DJango. You should not remove a field from a parent Django model in an inheriting model.#612191Training
@SrinivasReddyThatiparthy I have pointed this out already in the post.Dextrocular
Why not just override the username class attribute in your subclass ?​ it could even return instance.email.Unspotted
M
5

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.

Madriene answered 12/9, 2013 at 18:10 Comment(1)
I'm looking at it from the reusability perspective, it's very common to use email as the primary user field an in order to do that you will need to do what you mentioned, which is somehow a long solution for a common problem. It's probably a better idea to generalize AbstractUser a bit further and allow its subclass to choose between username or email as the primary field.Dextrocular
S
0

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

Skintight answered 8/8 at 22:44 Comment(0)
N
-14

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.

Nila answered 31/7, 2015 at 21:16 Comment(4)
This is very bad solution in sooo many ways.. I know this post is quite old, but I'd want to warn others not to even try to do that. Editing Django source code or any other vendor library (that lives outside you own codebase, e.g. virtualenv) is never the right way to go. Just remember it, don't even try to argue. Just look at the above linked article to find better solution.Esquire
OOoooof Not even close to any of the suggested methodology., NEVER alter the Source code like this.Archiplasm
+1 on other comments, do NOT customize Django source code, ever. Bad practice. Ideally remove this answer pleaseBiology
Oh my goodness! Gravedigging here. +1Mccollum

© 2022 - 2024 — McMap. All rights reserved.