Django Extend User Model Example

Published On: 02/09/2022 | Category: Django


Hi Dev,

This article will provide example of how to extend django user model. step by step explain extending user model using a proxy model. if you have question about django extend user model example then I will give simple example with solution. if you have question about extending user model using a custom model extending abstractbaseuser then I will give simple example with solution. Alright, let’s dive into the steps.

Here, i will show you different strategies you can utilize to elongate the Django User Model. Nothing to worry about, you don’t have to do everything from scratch.

Four different ways to extend the existing Django user model so follow my below steps.

  1. Extending User Model Using a Proxy Model
  2. Extending User Model Using a One-To-One Link
  3. Extending User Model Using a Custom Model Extending AbstractBaseUser
  4. Extending User Model Using a Custom Model Extending AbstractUser
Step 1: Extending User Model using Proxy:

First of all in your mind raise a question What is Proxy Model: proxy model is a method in which you are not creating a new table in the database. It is used to change the behavior of a current model (e.g. adding new methods, etc.) without affecting the existing database.

And, When should I use a Proxy Model? you can use a Proxy model when you don’t need to store extra information in the database, but simply to change the model’s query manager or to add extra methods.

Let's See the below example:

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

class Person(User):
    objects = PersonManager()
    
    class Meta:
        proxy = True
        ordering = ('first_name', )
    
    def write_something(self):
        ... 

Above example we have defined a Proxy Model named Person. in Meta class, we are adding the property proxy = True, which tells Django this is a Proxy Model.

In above case, you can redefine the default ordering as shown in the example. I have assigned a custom Manager to the model, and also defined a new method write_something.

Step 2: Extending User Model Using a One-To-One Link:

In this second section we can learn about One-To-One link, the Django model is going to have its own database table and will hold a One-To-One relationship with the subsisting User Model through a OneToOneField

We will be creating a new Django Model to store some extra information that relates to the User Model.

models.py
from django.db import models
from django.contrib.auth.models import User

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(max_length=500, blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True) 

Now we will define signals so that our Profile model will be created/updated automatically when the User instance is created or updated.

from django.db import models
from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    bio = models.TextField(max_length=500, blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)

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

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

So, whenever a save event occurs, we are hooking the create_user_profile and save_user_profile methods to the User model. this kind of signal is called post_save.

How to use in django templates

<h2>{{ user.get_full_name }}</h2>
<ul>
  <li>Username: {{ user.username }}</li>
  <li>Location: {{ user.profile.location }}</li>
  <li>Birth Date: {{ user.profile.birth_date }}</li>
</ul>
views.py

How about inside a view method?

def update_profile(request, user_id):
    user = User.objects.get(pk=user_id)
    user.profile.bio = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit...'
    user.save()

You will never have to call the Profile’s preserve method. Everything is done through the User model.

What if I’m using Django Forms?

forms.py
class UserForm(forms.ModelForm):
    class Meta:
        model = User
        fields = ('first_name', 'last_name', 'email')

class ProfileForm(forms.ModelForm):
    class Meta:
        model = Profile
        fields = ('url', 'location', 'company') 
views.py
class ProfileUpdateView(LoginRequiredMixin, TemplateView):
    user_form = UserForm
    profile_form = ProfileForm
    template_name = 'common/profile-update.html'

    def post(self, request):

        post_data = request.POST or None

        user_form = UserForm(post_data, instance=request.user)
        profile_form = ProfileForm(post_data, instance=request.user.profile)

        if user_form.is_valid() and profile_form.is_valid():
            user_form.save()
            profile_form.save()
            messages.success(request, 'Your profile was successfully updated!')
            return HttpResponseRedirect(reverse_lazy('profile'))

        context = self.get_context_data(
                                        user_form=user_form,
                                        profile_form=profile_form
                                    )

        return self.render_to_response(context)     

    def get(self, request, *args, **kwargs):
        return self.post(request, *args, **kwargs)
profile.html
<form method="post">
  {% csrf_token %}
  {{ user_form.as_p }}
  {{ profile_form.as_p }}
  <button type="submit">Save changes</button>
</form> 

Sometimes, there are quandaries that can be caused, like firing hundreds or thousands of queries. This quandary can be mitigated utilizing the select_related method.

users = User.objects.all().select_related('profile')
Step 3: Extending User Custom Model using AbstractBaseUser:

So, in this section extending user model using AbstractBaseUser, what is custom user model extending AbstractBaseUser : it is an entirely new User model that inherits from AbstractBaseUser. There are special things you need to take care of i.e update some references in settings.py at the beginning of the project.

There was a situation for me where I had to use an email address as the authentication token instead of a username. Also, there was no need for the is_staff flag, as I wasn’t using the Django Admin.

See the below model I had to define:

models.py
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 User(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)
    is_staff = models.BooleanField(_('staff'), 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) 

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

So, here’s my UserManager looks like:

UserManager
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 clean up of the existing UserManager, removing the is_staff property and the username.

Now finally we have to update our settings.py. In settings.py defining 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?

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 will be publicly available, it is strongly advised to use the following method:

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)
Step 4: Extending User Custom Model Using Extending AbstractUser:

In this last one is we can learn about What is a Custom User Model Extending AbstractUser? : It is a new User model that inherits from AbstractUser. There are special things you need to take care of i.e update some references in settings.py at the beginning of the project.

The class django.contrib.auth.models.AbstractUser provides the full implementation of the default User as an abstract model which is a very straightforward way.

models.py
from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    bio = models.TextField(max_length=500, blank=True)
    location = models.CharField(max_length=30, blank=True)
    birth_date = models.DateField(null=True, blank=True)

After this we have to update our settings.py defining the AUTH_USER_MODEL property as:

AUTH_USER_MODEL = 'core.User' 

I hope it will help you....