Extending user model using a one-to-one link, how to extend django user model, extends user model django, extend django user model, abstractbaseuser django example, extend django, extending the user model django, django extends user model.
Django’s built-in authentication system is excellent. We can use it out-of-the-box, which can result in saving a lot of development and testing time. It is extremely safe and fits most of the use cases. But sometimes we need to extend some functionalities so to fit our Web application.
Commonly you want to store some more data or fields related to the user. If your Web application has an eCommerce appeal, you might want to store payment info, the location of the user, address and other things like that.
In this tutorial, I will show you different strategies you can use to extend the Django User Model. Nothing to worry about, you don’t have to do everything from scratch.
Ways to Extend the Existing User Model
Here, I am going to show you 4 different ways to extend the existing Django user model.
Option 1: Extending User Model Using a Proxy Model
What is a Proxy Model?
The 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.
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.
That’s what I need! – Take me to the instructions.
Video Tutorial – Take me to the instructions.
Option 2: Extending User Model Using a One-To-One Link
What is a One-To-One Link?
In 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 existing User Model through a OneToOneField
When should I use a One-To-One Link?
One-To-One Link can be used when you need to collect extra information about the existing User Model that’s not related to the authentication process. For example, User Profile.
That’s what I need! – Take me to the instructions.
Option 3: Extending User Model Using a Custom Model Extending AbstractBaseUser
What is a 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.
When should I use a Custom User Model Extending AbstractBaseUser?
You can use a Custom User Model when your application has specific requirements in relation to the authentication process. For example, using an email address as your identification instead of a username.
That’s what I need! – Take me to the instructions.
Option 4: Extending User Model Using a Custom Model Extending AbstractUser
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.
When should I use a Custom User Model Extending AbstractUser?
You can use it when you are happy with the way Django handles the authentication process and you wouldn’t change anything on it. Yet, you want to add some additional information straight away in the User model, without having to create an extra class (like in 2nd Option).
That’s what I need! – Take me to the instructions.
Extending User Model Using a Proxy Model
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 do_something(self): ...
In this 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.
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 do_something.
Note that User.objects.all() and Person.objects.all() will query the same database table. The only difference will be the behavior we define for the Proxy Model.
Extending User Model Using a One-To-One Link
We will be creating a new Django Model to store some extra information that relates to the User Model.
Create a Model Profile:
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()
Here, 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.
Let’s see how we can use it.
See the below example of Dango template:
<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>
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 save method. Everything is done through the User model.
What if I’m using Django Forms?
Did you know that you can process more than one form at once? Check out this snippet below:
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 problems that can be caused, like firing hundreds or thousands of queries. This problem can be mitigated using the select_related method.
Knowing beforehand you will need to access a related data, you can prefetch it in a single database query below:
users = User.objects.all().select_related('profile')
Extending User Model Using a Custom Model Extending AbstractBaseUser
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:
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)
Since we are inheriting from the AbstractBaseUser we have to follow some rules in order to execute:
- USERNAME_FIELD: It is 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: It is a list of the field names that will be prompted when creating a user via the createsuperuser 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 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:
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?
There are two ways. Considering 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 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)
Extending User Model Using a Custom Model Extending AbstractUser
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.
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'
This is a similar way as done in the previous method. Keep in mind that this should be done ideally at the beginning with extra care, as it will change the whole database schema. Also, create foreign keys to the User model. Import the settings from django.conf import settings and refer them to the settings.AUTH_USER_MODEL instead of referring directly to the custom User model.