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)
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.UserProfile' in 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:
unique=Trueset in its definition);
Okay, let’s move forward. I had also to define my own
UserManager. That’s because the existing manager defines the
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
Now the final move. We have to update our settings.py. More specifically the
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
How should I reference this model?
Well, there are two ways. Consider a model named
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
objects = PersonManager()
proxy = True
ordering = ('first_name', )
That's all about Using Modle.
Check the full code on Github
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 !!!!