0% found this document useful (0 votes)
55 views

Django - Creating A Custom User Model

The document discusses creating a custom user model in Django. It describes extending the default User model by adding a one-to-one relationship profile model to store additional user information. It also covers creating a completely new user model by specifying it in the AUTH_USER_MODEL setting and defining a custom user model class, custom user manager, and making migrations. The custom user model allows using alternative authentication sources and modifying default user fields like using email instead of username.

Uploaded by

Arala Fola
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
55 views

Django - Creating A Custom User Model

The document discusses creating a custom user model in Django. It describes extending the default User model by adding a one-to-one relationship profile model to store additional user information. It also covers creating a completely new user model by specifying it in the AUTH_USER_MODEL setting and defining a custom user model class, custom user manager, and making migrations. The custom user model allows using alternative authentication sources and modifying default user fields like using email instead of username.

Uploaded by

Arala Fola
Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 15

Creating a Custom User

Model in Django
The built-in User model and authentication functionality in Django are fantastic.
For a long time, it’s been a Django mainstay. It is Django’s source of preference
over alternative web frameworks like Flask, FastAPI, AIOHttp, and others. When a
username and password contained in the user model need to be authenticated
against a service other than Django’s default, authentication backends provide an
expandable system.
You can grant specific permissions to your models, which can be validated using

Django’s authorization system.

You’ll need to alter the default user model’s functionality now and then. Assume

you no longer require a username field. Alternatively, you may make the email

field your default “username” field. We’ll need to tweak our regular Django User

model for this.

Remember that changing Django’s defaults adds a lot of complexity to an already

complicated system. When possible, stick to the original settings. In other words,

if you don’t have to, don’t develop a unique user model; Foreign Keys are perfect

for that.

You can either extend the default User model or create a new one. In this article,

we will opt to go with the last-mentioned.

Other sources of authentication


There are instances to connect to another authentication source, such as a

database of users and passwords or a means of authentication. For example, your

organization may already have an LDAP server set up that maintains each
employee’s username and password. If users had different accounts in LDAP and

Django-based applications, it would be inconvenient for both the network

administrator and the users.

As a result, the Django authentication system allows you to plug in alternative

authentication sources to handle this scenario. You can either change Django’s

default database-based strategy or use it with other systems.

Extending the User model that already exists


There are two methods for extending the basic User model without creating your

own. You can develop a proxy model based on User if the changes you need are

simply behavioral and don’t require any modifications to the database. It allows

for any proxy model’s characteristics, such as default ordering, custom managers,

and custom model methods.

You can use an OneToOneField to a model containing the fields for additional

information if you want to store a user’s information. Because it may keep non-

auth-related information about a site user, this one-to-one approach is commonly

referred to as a profile model. You could, for example, establish a Staff model:

from django.contrib.auth.models import User

class Staff(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
department = models.CharField(max_length=100)

Using Django’s standard related model conventions, you may retrieve the related
information for an existing Staff Ann Keen, who has both a User and an Employee
model:
u = User.objects.get(username='ann')
anns_department = u.employee.department

In your app’s admin.py, define an InlineModelAdmin. For this example, we’ll use a
StackedInline and add it to a UserAdmin class that is registered with the User
class: To add a profile model’s fields to the user page in the admin, define an
InlineModelAdmin and add it to a UserAdmin class that is registered with the User
class:

from django.contrib import admin


from django.contrib.auth.admin import UserAdmin as MainUserAdmin
from django.contrib.auth.models import User as CustomUser

from my_user_profile_app.models import Staff

# It is the Staff's inline admin model acting like a singleton


class StaffInline(admin.StackedInline):
model = Staff
can_delete = False
verbose_name_plural = 'staff'

# Definition of a new User admin

class UserAdmin(MainUserAdmin):
inlines = (StaffInline,)

# Re-register UserAdmin

admin.site.unregister(CustomUser)
admin.site.register(CustomUser, UserAdmin)
These profile models are nothing remarkable; they’re just Django models with a
one-to-one relationship with a user model. As a result, they aren’t produced
automatically when a user is created; however, a
django.db.models.signals.post_save might be used to create or update relevant
models as needed.
Using related models necessitates more queries or joins to retrieve the relevant

data. Depending on your needs, a bespoke user model with the associated fields

may be a preferable solution; however, existing relationships to the default user

model within your project’s apps may justify the increased database burden.

Creating a whole new app


Some applications may have authentication requirements that Django’s built-in

User model isn’t always suitable. For example, using an email address rather than

a username as your identity token makes more sense on some websites.

By supplying a value for the AUTH_USER_MODEL parameter that refers to a

custom model, Django allows you to alter the default user model. It is where you’ll

store your custom user model, and in our case, we will call it accounts. It explains

the name of the Django model you want to use as your user model and the label

of the Django app, which must be in your INSTALLED_APPS.

python manage.py startapp accounts

You may either use the Django example or the one we’ve provided below. We’ll
make it easier for you here.

# accouts/models.py

from django.db import models


from django.contrib.auth.models import (
BaseUserManager, AbstractBaseUser
)

class User(AbstractBaseUser):
email = models.EmailField(
verbose_name='user email address',
max_length=100,
unique=True,
)
is_active = models.BooleanField(default=True)
is_staff = models.BooleanField(default=False)

is_admin = models.BooleanField(default=False) # a superuser

# notice the absence of a "Password field" that is built-in.

USERNAME_FIELD = 'email'
REQUIRED_FIELDS = [] # Email & Password are required by default.

def get_full_name(self):
# users identification is by email
return self.email

def get_user_short_name(self):
# user identification is by email
return self.email

def __str__(self):
return self.email
def has_perm(self, perm, obj=None):
# return true if user has the necessary permissions
return True

def has_module_perms(self, app_label):


# return true if user is granted the permission to view 'app_label'
return True

@property
def is_staff(self):
# returns true if user is a staff
return self.is_staff

@property
def is_admin(self):
# returns true if user is admin member
return self.is_admin

So, what precisely is the USERNAME_FIELD? That’ll be how Django will recognize
this user. It takes the place of the built-in username field with whatever you
specify. In this situation, we choose to use an EmailField, but you could instead
use a phone number.

Create the User Model Manager


For your user model, you need to additionally create a custom manager. You can

use Django’s UserManager if your user model has the same email, is_staff, date

joined fields, is_active, is_superuser, last_login, and username as Django’s default

user. However, if your user model has different fields, you’ll need to define a
custom manager that extends BaseUserManager and provides two additional

methods:

create_user(username_field, password=None, **additional_fields)


create_superuser(username_field, password=None, **additional_fields)

The User Manager in Django has built-in methods. For our custom user model to
perform correctly, we must customize them.

class UserManager(BaseUserManager):

def create_user(self, email, password=None):


"""
responsible for both creating and saving a user with the given parameters

"""
if not, email:
raise ValueError('Cannot create a user without an email address ')

user = self.model(
email=self.normalize_email(email),
)

user.set_password(password)
user.save(using=self._db)
return user

def create_staffuser(self, email, password):


"""
Creates and saves a staff user with the given email and password.
"""
user = self.create_user(
email,
password=password,
)
user.staff = True
user.save(using=self._db)
return user

def create_superuser(self, email, password):


"""
Responsible for creating and saving a superuser with the given parameters.
"""
user = self.create_user(
email,
password=password,
)
user.staff = True
user.admin = True
user.save(using=self._db)
return user

# hook in the New Manager to our Model


class User(AbstractBaseUser): # from step 2
...
objects = UserManager()
Update settings module
Run migrations

python manage.py makemigrations


python manage.py migrate

update settings.py
AUTH_USER_MODEL = 'accounts.User'

Run migrations one more time


python manage.py makemigrations
python manage.py migrate

python manage.py createsuperuser

Setting AUTH_USER_MODEL allows us to use a variety of third-party packages that


use the Django user model, such as Django Rest Framework, Django AllAuth, and
Python Social Auth.
Now, whenever you require the user model, type:

from django.contrib.auth import get_user_model


User = get_user_model()

It gives other developers and our future selves the kind of peace of mind that only
well-written code can deliver. In fact, get_user_model is one of those components
that the core Django developers settled on a long time ago. But what about
foreign keys created by users? You’ll come across this issue at some point:

class SomeModel(models.Model):
user = models.ForeignKey(…)
You’ll make use of the option settings. AUTH USER MODEL every time, regardless
of whether the user model has been customized. As a result, it will appear like
this:

from django.conf import settings

User = settings.AUTH_USER_MODEL

class SomeModel(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)

Create the registration, change, and admin-level forms


We can utilize the built-in User Creation Form now that we’ve altered the

AUTH_USER_MODEL because it’s a model form based on our user model, but let’s

learn how to construct our own anyhow.

# accounts/forms.py

from django import forms


from django.contrib.auth import get_user_model
from django.contrib.auth.forms import ReadOnlyPasswordHashField

User = get_user_model()

class RegisterForm(forms.ModelForm):
"""
The default

"""
password = forms.CharField(widget=forms.PasswordInput)
password_2 = forms.CharField(label='Confirm Password',
widget=forms.PasswordInput)

class Meta:
model = User
fields = ['email']

def clean_email(self):
'''
Verify email is available.
'''
email = self.cleaned_data.get('email')
qs = User.objects.filter(email=email)
if qs.exists():
raise forms.ValidationError("email is taken")
return email

def clean(self):
'''
Verify both passwords match.
'''
cleaned_data = super().clean()
password = cleaned_data.get("password")
password_2 = cleaned_data.get("password_2")
if password is not None and password != password_2:
self.add_error("password_2", "Your passwords must match")
return cleaned_data
class UserAdminCreationForm(forms.ModelForm):
"""
A form for creating new users. Includes all the required
fields, plus a repeated password.
"""
password = forms.CharField(widget=forms.PasswordInput)
password_2 = forms.CharField(label='Confirm Password',
widget=forms.PasswordInput)

class Meta:
model = User
fields = ['email']

def clean(self):
'''
Verify both passwords match.
'''
cleaned_data = super().clean()
password = cleaned_data.get("password")
password_2 = cleaned_data.get("password_2")
if password is not None and password != password_2:
self.add_error("password_2", "Your passwords must match")
return cleaned_data

def save(self, commit=True):


# Saving the given password as a hash
user_info = super().save(commit=False)
user_info.set_password(self.cleaned_data["password"])
if commit:
user_info.save()
return user_info

class UserAdminChangeForm(forms.ModelForm):
"""
The form is responsible for user updates and has all user fields
. It also uses the admin's
password hash for display.
"""
password = ReadOnlyPasswordHashField()

class Meta:
model = User
fields = ['email', 'password', 'is_active', 'admin']

def clean_password(self):
# return the first value irrespective of what is provided by the user
return self.initial["password"]

Updating Django Admin


Let’s put our forms as mentioned above to good use.

from django.contrib import admin


from django.contrib.auth import get_user_model
from django.contrib.auth.models import Group
from django.contrib.auth.admin import UserAdmin as MainUserAdmin

from .forms import UserAdminCreationForm, UserAdminChangeForm


User = get_user_model()
# Remove Group Model from admin. We're not using it.

admin.site.unregister(Group)

class UserAdmin(MainUserAdmin):
# These forms forms to add and change user instances
form = UserAdminChangeForm
add_form = UserAdminCreationForm

# The fields to be used in displaying the User model.


# These override the definitions on the base UserAdmin
# that reference specific fields on auth.User.
list_display = ['email', 'admin']
list_filter = ['admin']
fieldsets = (
(None, {'fields': ('email', 'password')}),
('Personal info', {'fields': ()}),
('Permissions', {'fields': ('admin',)}),
)
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
# overrides get_fieldsets to use this attribute when creating a user.
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2')}
),
)
search_fields = ['email']
ordering = ['email']
filter_horizontal = ()

admin.site.register(User, UserAdmin)

Models for custom users and proxy users


Installing a custom user model will break any proxy model extending User, which

is one of the limitations of custom user models. Proxy models must have a

concrete base class; establishing a custom user model disables Django’s ability to

identify the base class reliably.

If your project uses proxy models, you must either adapt the proxy to extend the

user model in use or merge the behavior of your proxy into your User subclass.

Conclusion
Although the authentication provided by Django is enough in most circumstances,

you may have requirements that are not covered by the defaults. Understanding

what parts of the supplied system are expandable or replaceable is necessary for

customizing authentication in your projects. This document has clearly explained

how the authentication system is changed.

https://www.codeunderscored.com/creating-a-custom-user-model-in-django/

You might also like