How to Extend Django Inbuilt User Model



Whenever we build an application using Django/ REST API  we need User model to store the information of the user, and we can create our own customization model for that, but if you use existing (Inbuild) user model you'll get a lot of functionality that goes along with it -- for example a pretty good permission system, and you can be sure to be compatible with all third-party modules. But if you think you'll need to expand on the User model, it's pretty straightforward how to do it. You might find that in the future you need to add more methods to your model than you expected. In this tutorial we are going to see how to extend the User Model in our application, and use of that 

 



Ways to Extend User Model:

Generally, 4 ways are described to extend the Django User Model depend on situations below are, 

1) Using One-To-One Relation with User Model
2) By Extending AbstractUser Model
3) By Extending AbstractUser 
4) Using Proxy Model

 


 

Using One-To-One Relation with User Model:

In this type, we must have our own database model with the user field so that it will hold the existing User model via the OneToOne relationship.
Well, this is the method I used most of the time in my application because I can extend the Use model and can able to describe the more field like location, bio and many more which are not with the existing user.

It will more clear by seeing the below snippet,
I considered UserProfile is my own database and the user is my field which is going to hold an existing User model via  OneToOne field,

 

class UserProfile(models.Model):
    """
    Model to store user detail with extending the default User model

    `User`
        User Instance

    `location`
        Location of user
    """

    user = models.OneToOneField(User, on_delete=models.CASCADE)
    location = models.CharField(max_length=20)

 

Now we have to do something so it will create/update our UserProfile, whenever User instance is created in the existing model, and that can be achieved with signals i.e. post_save with  create_user_profile and  save_user_profile methods

 

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        UserProfile.objects.create(user=instance)


@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    instance.userprofile.save()

 

Greate That's it now we can create the user and update it through admin panel, 

 

class userprofileadmin(admin.ModelAdmin):
    list_display = ['user', 'location']


admin.site.register(UserProfile, userprofileadmin)

 

Results:

Here I created a few User using createsuperuser command via manage.py 

And those are extended in my applications, Now I can choose existing User and update the location for them which is really great !!!!



 

By Extending AbstractUser Model:

This is a really straightforward way since we need to just extend the AbstractUser model to our class with importing from  django.contrib.auth.models as below 

 

from django.contrib.auth.models import User, AbstractUser

class UserProfile(AbstractUser):
    location = models.CharField(max_length=30, blank=True)

 

and you need to update  AUTH_USER_MODEL = 'core.UserProfilein setting


By Extending AbstractUser 

Well, honestly I avoid doing it at all costs. But sometimes you can’t run from it. And it is perfectly fine. There is hardly such a thing as the best or worst solution. For the most part, there is a more or less appropriate solution. If this is the most appropriate solution for your case, go ahead.

I needed to use an email address as auth token and in the scenario the was completely useless for me. Also, there was no need for the is_staff flag, as I wasn’t using the Django Admin.

Here is how I defined my own user model:

from __future__ import unicode_literals

from django.db import models
from django.core.mail import send_mail
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser
from django.utils.translation import ugettext_lazy as _

from .managers import UserManager


class UserProfile(AbstractBaseUser, PermissionsMixin):
    email = models.EmailField(_('email address'), unique=True)
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
    date_joined = models.DateTimeField(_('date joined'), auto_now_add=True)
    is_active = models.BooleanField(_('active'), default=True)
    avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)

    objects = UserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')

    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)

 

I wanted to keep it as close as possible to the existing User model. Since we are inheriting from the AbstractBaseUser we have to follow some rules:

  • USERNAME_FIELD: A string describing the name of the field on the User model that is used as the unique identifier. The field must be unique (i.e., have unique=True set in its definition);
  • REQUIRED_FIELDS: A list of the field names that will be prompted for when creating a user via the createsuperuser management command;
  • is_active: A boolean attribute that indicates whether the user is considered “active”;
  • get_full_name(): A longer formal identifier for the user. A common interpretation would be the full name of the user, but it can be any string that identifies the user.
  • get_short_name(): A short, informal identifier for the user. A common interpretation would be the first name of the user.

Okay, let’s move forward. I had also to define my own UserManager. That’s because the existing manager defines the create_user and create_superuser methods.

So, here is what my UserManager looks like:

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)

 

Basically I’ve done a clean up of the existing UserManager, removing the username and the is_staff property.

Now the final move. We have to update our settings.py. More specifically the AUTH_USER_MODEL property.

AUTH_USER_MODEL = 'core.User'

This way we are telling Django to use our custom model instead of the default one. In the example above, I’ve created the custom model inside an app named core.

How should I reference this model?

Well, there are two ways. Consider a model named Course:

from django.db import models
from testapp.core.models import User

class Course(models.Model):
    slug = models.SlugField(max_length=100)
    name = models.CharField(max_length=100)
    tutor = models.ForeignKey(User, on_delete=models.CASCADE)

 

This is perfectly okay. But if you are creating a reusable app, that you want to make available for the public, it is strongly advised that you use the following strategy:

from django.db import models
from django.conf import settings

class Course(models.Model):
    slug = models.SlugField(max_length=100)
    name = models.CharField(max_length=100)
    tutor = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)

 


Using Proxy Model

If you don't want to store extra information in your database just you want to extend as it is then you can follow this method, it is a very easy and simple one.
In this method, we are going to use one model inheritance concept i.e Proxy (Which won't create any extra table but side by side only extend the user ).
A less intrusive way to extend the user model but very limited to use 

 

from django.contrib.auth.models import User
from .managers import PersonManager

class UserProfile(User):
    objects = PersonManager()

    class Meta:
        proxy = True
        ordering = ('first_name', )

 

 

That's all about Using Modle.
Check the full code on Github



Conclusion: 

So as we saw four ways of extending the User Model among that we can adopt anyone depending on our requirements but usually I follow by OneToOne relationship where I can able to describe any extra field for my User model and its very easy to trace in using ORM concept ...

Happy Pythoning !!!!

 

 

 


© 2020 Blog, All rights reserved.   This work is licensed under  Creative Commons License
Top