What's the best way to extend the User model in Django?
Asked Answered
A

17

548

What's the best way to extend the User model (bundled with Django's authentication app) with custom fields? I would also possibly like to use the email as the username (for authentication purposes).

I've already seen a few ways to do it, but can't decide on which one is the best.

Alvera answered 4/9, 2008 at 16:19 Comment(4)
Most answer are outdated/deprecated. Please see stackoverflow.com/a/22856042/781695 & https://mcmap.net/q/74636/-django-1-5-user-and-additional-info/781695 & https://mcmap.net/q/74637/-django-1-5-user-model-relationship/781695Coretta
@buffer - The first question you linked to (stackoverflow.com/a/22856042/781695) has now been deleted. The others are still valid.Onshore
learnbatta.com/blog/using-custom-user-model-in-django-23Hafner
This blog post is useful: dontrepeatyourself.org/post/…Tedium
E
311

The least painful and indeed Django-recommended way of doing this is through a OneToOneField(User) property.

Extending the existing User model

If you wish to store information related to User, you can use a one-to-one relationship to a model containing the fields for additional information. This one-to-one model is often called a profile model, as it might store non-auth related information about a site user.

That said, extending django.contrib.auth.models.User and supplanting it also works...

Substituting a custom User model

Some kinds of projects may have authentication requirements for which Django’s built-in User model is not always appropriate. For instance, on some sites it makes more sense to use an email address as your identification token instead of a username.

[Ed: Two warnings and a notification follow, mentioning that this is pretty drastic.]

I would definitely stay away from changing the actual User class in your Django source tree and/or copying and altering the auth module.

Experiment answered 4/9, 2008 at 17:2 Comment(13)
With this solution should you ForeignKey to User or Profile?Eliza
FYI the new (1.0+) recommended method is OneToOneField(User) docs.djangoproject.com/en/dev/topics/auth/…Scabbard
Shawn Rider of PBS gave some really good reasons why you should not extend django.contrib.auth.models.User. Use OneToOneField(User) instead.Benevolent
user = models.ForeignKey(User, unique=True) is this the same as user = models.OneToOneField(User) ? I would think the end effect is the same? But maybe the implementation in the backend is different.Insubstantial
@pydanny: actually he gave an argument for not overriding the user modelImportunacy
Can anyone link to Shawn Riders argument/reasoning for that?Maryrosemarys
@JeremyBlanchard: I did some digging and the best I could find is a single bullet point from this: birdhouse.org/blog/2010/09/13/djangocon-2010 "you’ll have to patch every re-usable app you pull in"Reconstruct
@JeremyBlanchard: The full video is here: blip.tv/djangocon/… The patching argument is the only one he makes; he didn't mention any other problems, and seemed to suggest it's not a deal-breaker (they still have some of that code in their system).Reconstruct
@DaveForgac the link seems broken, more details here docs.djangoproject.com/en/1.4/topics/auth/…Rodmann
@SamStoelinga from:(docs.djangoproject.com/en/dev/ref/models/fields/#onetoonefield) A one-to-one relationship. Conceptually, this is similar to a ForeignKey with unique=True, but the “reverse” side of the relation will directly return a single object.Nevers
Here is some additional info about extending user models as of the django 1.7 docsCandice
Can't I just created a new class inherited from django.contrib.auth.models.User if I only need a couple of additional fields for my User?Passport
i don't know about you, but for me, OneToOneField(User) is very annoying for big projects! i prefer using O.O.P. (extending) in my projects!Minoan
C
240

Note: this answer is deprecated. see other answers if you are using Django 1.7 or later.

This is how I do it.

#in models.py
from django.contrib.auth.models import User
from django.db.models.signals import post_save

class UserProfile(models.Model):  
    user = models.OneToOneField(User)  
    #other fields here

    def __str__(self):  
          return "%s's profile" % self.user  

def create_user_profile(sender, instance, created, **kwargs):  
    if created:  
       profile, created = UserProfile.objects.get_or_create(user=instance)  

post_save.connect(create_user_profile, sender=User) 

#in settings.py
AUTH_PROFILE_MODULE = 'YOURAPP.UserProfile'

This will create a userprofile each time a user is saved if it is created. You can then use

  user.get_profile().whatever

Here is some more info from the docs

http://docs.djangoproject.com/en/dev/topics/auth/#storing-additional-information-about-users

Update: Please note that AUTH_PROFILE_MODULE is deprecated since v1.5: https://docs.djangoproject.com/en/1.5/ref/settings/#auth-profile-module

Cogitative answered 8/6, 2009 at 16:53 Comment(8)
Thanks for the clear example, note that def create_user.... is not part of the UserProfile class and should be aligned left.Vassili
With this solution should other models ForeignKey to User or UserProfile?Eliza
Other models should use user = models.ForeignKey( User ), and retrieve the profile object via user.get_profile(). Remember to from django.contrib.admin.models import User.Flowerage
By using this method, I need to separate when I retrieve usual information (name, password) and custom one or is there a way to do it at once ? Same for the creation a new user ?Psalms
Can someone explain the arguments taken by create_user_profile??Hack
This answer & comments has become outdated e.g. AUTH_PROFILE_MODULE is deprecated, UserCoretta
The AUTH_PROFILE_MODULE setting, and the get_profile() method on the User model, will be removed in Django 1.7.Catapult
You shouldn't link to /dev/ in the django docs, because the /dev/ branch of the docs is always changing. The link posted for #storing-additional-information-about-users no longer arrives at a topic about that.Echelon
O
223

Well, some time passed since 2008 and it's time for some fresh answer. Since Django 1.5 you will be able to create custom User class. Actually, at the time I'm writing this, it's already merged into master, so you can try it out.

There's some information about it in docs or if you want to dig deeper into it, in this commit.

All you have to do is add AUTH_USER_MODEL to settings with path to custom user class, which extends either AbstractBaseUser (more customizable version) or AbstractUser (more or less old User class you can extend).

For people that are lazy to click, here's code example (taken from docs):

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


class MyUserManager(BaseUserManager):
    def create_user(self, email, date_of_birth, password=None):
        """
        Creates and saves a User with the given email, date of
        birth and password.
        """
        if not email:
            raise ValueError('Users must have an email address')

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

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

    def create_superuser(self, username, date_of_birth, password):
        """
        Creates and saves a superuser with the given email, date of
        birth and password.
        """
        u = self.create_user(username,
                        password=password,
                        date_of_birth=date_of_birth
                    )
        u.is_admin = True
        u.save(using=self._db)
        return u


class MyUser(AbstractBaseUser):
    email = models.EmailField(
                        verbose_name='email address',
                        max_length=255,
                        unique=True,
                    )
    date_of_birth = models.DateField()
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)

    objects = MyUserManager()

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['date_of_birth']

    def get_full_name(self):
        # The user is identified by their email address
        return self.email

    def get_short_name(self):
        # The user is identified by their email address
        return self.email

    def __unicode__(self):
        return self.email

    def has_perm(self, perm, obj=None):
        "Does the user have a specific permission?"
        # Simplest possible answer: Yes, always
        return True

    def has_module_perms(self, app_label):
        "Does the user have permissions to view the app `app_label`?"
        # Simplest possible answer: Yes, always
        return True

    @property
    def is_staff(self):
        "Is the user a member of staff?"
        # Simplest possible answer: All admins are staff
        return self.is_admin
Orozco answered 28/9, 2012 at 22:22 Comment(8)
The create_user function doesn't seem to store the username, how so?!Shizukoshizuoka
Because in this example, email is the username.Dialectic
You need to add unique=True to the email field to let USERNAME_FIELD accept itSulfapyridine
Hi, I was trying to create custom user as you said but couldn't able to login using email of custom user email. would you not mind to say why?Cherri
I can't really help you without specifics. It'd be better if you created new question.Dialectic
get the following error when we run syncd which lead to create super user: TypeError: create_superuser() got an unexpected keyword argument 'email'Karonkaross
The custom fields are normal model fields, you can access them from templates using eg. user.custom_field.Dialectic
anyone IF I need a user profile, should I extend this custom user (MyUser) e.g. like add more fields in existing MyUser or create new model e.g. Profile and then OneToOne link with MyUser.Stivers
A
63

Since Django 1.5 you may easily extend the user model and keep a single table on the database.

from django.contrib.auth.models import AbstractUser
from django.db import models
from django.utils.translation import ugettext_lazy as _

class UserProfile(AbstractUser):
    age = models.PositiveIntegerField(_("age"))

You must also configure it as current user class in your settings file

# supposing you put it in apps/profiles/models.py
AUTH_USER_MODEL = "profiles.UserProfile"

If you want to add a lot of users' preferences the OneToOneField option may be a better choice thought.

A note for people developing third party libraries: if you need to access the user class remember that people can change it. Use the official helper to get the right class

from django.contrib.auth import get_user_model

User = get_user_model()
Apocalypse answered 20/4, 2013 at 21:56 Comment(4)
If you plan on using django_social_auth, I recommend using a OneToOne relationship. DON'T use this method or it will mess up your migrations.Annemarie
@Annemarie : Could you elaborate or cite a referenceCoretta
@buffer, it was a long while back, but I think I tried combining the django_social_auth plugin and defining the AUTH_USER_MODEL to the social auth user. Then, when I ran manage.py migrate it messed up my app. When instead I used the social auth user model as a OneToOne relationship s described here: https://mcmap.net/q/74638/-foreignkey-vs-onetoone-field-django-duplicate/977116Annemarie
Probably has to do with Changing this setting after you have tables created is not supported by makemigrations and will result in you having to manually write a set of migrations to fix your schema Source : docs.djangoproject.com/en/dev/topics/auth/customizing/…Coretta
K
47

There is an official recommendation on storing additional information about users. The Django Book also discusses this problem in section Profiles.

Kiel answered 4/9, 2008 at 16:35 Comment(1)
On this page docs.djangoproject.com/en/dev/topics/auth/customizing got to the extending model sectionMayhem
C
26

The below one is another approach to extend an User. I feel it is more clear,easy,readable then above two approaches.

http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/

Using above approach:

  1. you don't need to use user.get_profile().newattribute to access the additional information related to the user
  2. you can just directly access additional new attributes via user.newattribute
Cavie answered 1/4, 2009 at 13:1 Comment(2)
I like Scott's approach much better, based on the inheritance of the User object rather than directly off the model. Can anyone say if this approach is not wise?Christman
@Christman - I just ran into this issue importing dump data, which appears to be a consequence of using this method: #8840568Onomasiology
K
20

You can Simply extend user profile by creating a new entry each time when a user is created by using Django post save signals

models.py

from django.db.models.signals import *
from __future__ import unicode_literals

class UserProfile(models.Model):

    user_name = models.OneToOneField(User, related_name='profile')
    city = models.CharField(max_length=100, null=True)

    def __unicode__(self):  # __str__
        return unicode(self.user_name)

def create_user_profile(sender, instance, created, **kwargs):
    if created:
        userProfile.objects.create(user_name=instance)

post_save.connect(create_user_profile, sender=User)

This will automatically create an employee instance when a new user is created.

If you wish to extend user model and want to add further information while creating a user you can use django-betterforms (http://django-betterforms.readthedocs.io/en/latest/multiform.html). This will create a user add form with all fields defined in the UserProfile model.

models.py

from django.db.models.signals import *
from __future__ import unicode_literals

class UserProfile(models.Model):

    user_name = models.OneToOneField(User)
    city = models.CharField(max_length=100)

    def __unicode__(self):  # __str__
        return unicode(self.user_name)

forms.py

from django import forms
from django.forms import ModelForm
from betterforms.multiform import MultiModelForm
from django.contrib.auth.forms import UserCreationForm
from .models import *

class ProfileForm(ModelForm):

    class Meta:
        model = Employee
        exclude = ('user_name',)


class addUserMultiForm(MultiModelForm):
    form_classes = {
        'user':UserCreationForm,
        'profile':ProfileForm,
    }

views.py

from django.shortcuts import redirect
from .models import *
from .forms import *
from django.views.generic import CreateView

class AddUser(CreateView):
    form_class = AddUserMultiForm
    template_name = "add-user.html"
    success_url = '/your-url-after-user-created'

    def form_valid(self, form):
        user = form['user'].save()
        profile = form['profile'].save(commit=False)
        profile.user_name = User.objects.get(username= user.username)
        profile.save()
        return redirect(self.success_url)

addUser.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <form action="." method="post">
            {% csrf_token %}
            {{ form }}     
            <button type="submit">Add</button>
        </form>
     </body>
</html>

urls.py

from django.conf.urls import url, include
from appName.views import *
urlpatterns = [
    url(r'^add-user/$', AddUser.as_view(), name='add-user'),
]
Khanate answered 25/6, 2016 at 3:9 Comment(4)
Hi and thanks for this answer. I am still struggling to understand how to link it all together in urls.py..any hints?Mispickel
@Mispickel Added urls example you can check nowKhanate
Thanks!!. It helps me a lot. Good exampleReflect
@AtulYadav .. what is the version of Django that you used ?Steradian
L
17

Extending Django User Model (UserProfile) like a Pro

I've found this very useful: link

An extract:

from django.contrib.auth.models import User

class Employee(models.Model):
    user = models.OneToOneField(User)
    department = models.CharField(max_length=100)

>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department
Lingam answered 13/4, 2016 at 8:9 Comment(1)
Done. I don't uderstand why a -1. Better an edit than a downvote in this case.Lingam
G
13

It's very easy in Django version 3.0+ (If you are NOT in the middle of a project):

In models.py

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

class CustomUser(AbstractUser):
    extra_field=models.CharField(max_length=40)

In settings.py

First, register your new app and then below AUTH_PASSWORD_VALIDATORS add

AUTH_USER_MODEL ='users.CustomUser'

Finally, register your model in the admin, run makemigrations and migrate, and it will be completed successfully.

Official doc: https://docs.djangoproject.com/en/3.2/topics/auth/customizing/#substituting-a-custom-user-model

Gratiana answered 21/4, 2021 at 22:22 Comment(2)
it could be not so simple, if you are in the middle of the project, some details can be found in relevant Django ticket code.djangoproject.com/ticket/25313#comment:2Lock
This is only a part of the story. Once you've created a custom user model, you have to separately also create a user manager, and UserCreationForm.Possess
A
6

It's too late, but my answer is for those who search for a solution with a recent version of Django.

models.py:

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)
    extra_Field_1 = models.CharField(max_length=25, blank=True)
    extra_Field_2 = models.CharField(max_length=25, 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()

you can use it in templates like this:

<h2>{{ user.get_full_name }}</h2>
<ul>
  <li>Username: {{ user.username }}</li>
  <li>Location: {{ user.profile.extra_Field_1 }}</li>
  <li>Birth Date: {{ user.profile.extra_Field_2 }}</li>
</ul>

and in views.py like this:

def update_profile(request, user_id):
    user = User.objects.get(pk=user_id)
    user.profile.extra_Field_1 = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit...'
    user.save()
Assurbanipal answered 12/10, 2020 at 6:8 Comment(5)
I become an AttributeError: 'User' object has no attribute 'profile' error when save_user_profile signal (instance.profile.save()) is fired. How did you solve this?Arnhem
@Arnhem have you imported the profile model?Assurbanipal
@Arnhem make sure the relation between your user and profile model is one to one not one to manyAngle
Which is 'a recent version of Django' referred to here?Earpiercing
@Earpiercing the latest version was Django 3.1 docs.djangoproject.com/en/3.1/releases/3.1Assurbanipal
R
4

New in Django 1.5, now you can create your own Custom User Model (which seems to be good thing to do in above case). Refer to 'Customizing authentication in Django'

Probably the coolest new feature on 1.5 release.

Regeniaregensburg answered 12/3, 2013 at 10:1 Comment(1)
Yes, indeed. But beware that one should avoid this unless necessary. Implementing your own for the reason in this question is perfectly valid though in case you're comfortable with the consequences documented. For simply adding fields a relationship with the regular User model is recommended.Overriding
C
4

Here I tried to explain how to extend Django's Default user model with extra fields It's very simple just do it.

Django allows extending the default user model with AbstractUser

Note:- first create an extra field model which you want to add in user model then run the command python manage.py makemigrations and python manage.py migrate

first run ---> python manage.py makemigrations then

second run python manage.py migrate

Step:- create a model with extra fields which you want to add in Django default user model (in my case I created CustomUser

model.py

from django.db import models
from django.contrib.auth.models import AbstractUser
# Create your models here.


class CustomUser(AbstractUser):
    mobile_no = models.IntegerField(blank=True,null=True)
    date_of_birth = models.DateField(blank=True,null=True)

add in settings.py name of your model which you created in my case CustomUser is the user model. registred in setttings.py to make it the default user model,

#settings.py

AUTH_USER_MODEL = 'myapp.CustomUser'

finally registred CustomUser model in admin.py #admin.py

@admin.register(CustomUser)
class CustomUserAdmin(admin.ModelAdmin):
    list_display = ("username","first_name","last_name","email","date_of_birth", "mobile_no")

then run command python manage.py makemigrations

then python manage.py migrate

then python manage.py createsuperuser

now you can see your model Default User model extended with (mobile_no ,date_of_birth)

enter image description here

Charteris answered 30/5, 2022 at 12:59 Comment(0)
E
3

This is what i do and it's in my opinion simplest way to do this. define an object manager for your new customized model then define your model.

from django.db import models
from django.contrib.auth.models import PermissionsMixin, AbstractBaseUser, BaseUserManager

class User_manager(BaseUserManager):
    def create_user(self, username, email, gender, nickname, password):
        email = self.normalize_email(email)
        user = self.model(username=username, email=email, gender=gender, nickname=nickname)
        user.set_password(password)
        user.save(using=self.db)
        return user

    def create_superuser(self, username, email, gender, password, nickname=None):
        user = self.create_user(username=username, email=email, gender=gender, nickname=nickname, password=password)
        user.is_superuser = True
        user.is_staff = True
        user.save()
        return user



  class User(PermissionsMixin, AbstractBaseUser):
    username = models.CharField(max_length=32, unique=True, )
    email = models.EmailField(max_length=32)
    gender_choices = [("M", "Male"), ("F", "Female"), ("O", "Others")]
    gender = models.CharField(choices=gender_choices, default="M", max_length=1)
    nickname = models.CharField(max_length=32, blank=True, null=True)

    is_active = models.BooleanField(default=True)
    is_staff = models.BooleanField(default=False)
    REQUIRED_FIELDS = ["email", "gender"]
    USERNAME_FIELD = "username"
    objects = User_manager()

    def __str__(self):
        return self.username

Dont forget to add this line of code in your settings.py:

AUTH_USER_MODEL = 'YourApp.User'

This is what i do and it always works.

Emanuelemanuela answered 25/7, 2018 at 5:0 Comment(0)
L
3

I recommend Substituting a custom User model which is more customizable than Extending the existing User model.

Substituting a custom User model:

  • can add extra fields.
  • can remove default fields.
  • can change default username and password authentication to email and password authentication.
  • must be the 1st migration to database otherwise there is error.

*You can see my answer explaining how to set up email and password authentication with AbstractUser or AbstractBaseUser and PermissionsMixin and you can see my answer and my answer explaining the difference between AbstractUser and AbstractBaseUser.

Extending the existing User model:

  • can add extra fields.
  • cannot remove default fields.
  • cannot change username and password authentication to email and password authentication.
  • doesn't need to be the 1st migration to database.

*You can see my answer explaining how to extend User model to add extra fields with OneToOneField().

Legato answered 19/7, 2023 at 5:41 Comment(0)
P
2

Simple and effective approach is models.py

from django.contrib.auth.models import User
class CustomUser(User):
     profile_pic = models.ImageField(upload_to='...')
     other_field = models.CharField()
Psalmist answered 22/2, 2020 at 18:36 Comment(3)
I would never use this solution. BUT as pure and really simplest solution this is the best and most correct for pure django way.Ethos
@ilyasJumadurdyew , why will 'never use this solution'? You claim it is the best and most correct, hence expected you embrace it.Earpiercing
I prefer to use completely custom model, and since modern systems has back and front separetely you won't be able to login in old way, also django has uncontrolled multiple login problem (you can't prevent parallel login out of the box)Ethos
P
1

Currently as of Django 2.2, the recommended way when starting a new project is to create a custom user model that inherits from AbstractUser, then point AUTH_USER_MODEL to the model.

Source: https://docs.djangoproject.com/en/2.2/topics/auth/customizing/#using-a-custom-user-model-when-starting-a-project

Phonoscope answered 18/11, 2019 at 19:18 Comment(0)
K
1

Try this:

Create a model called Profile and reference the user with a OneToOneField and provide an option of related_name.

models.py

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

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_profile')

    def __str__(self):
        return self.user.username

@receiver(post_save, sender=User)
def create_profile(sender, instance, created, **kwargs):
    try:
        if created:
            Profile.objects.create(user=instance).save()
    except Exception as err:
        print('Error creating user profile!')

Now to directly access the profile using a User object you can use the related_name.

views.py

from django.http import HttpResponse

def home(request):
    profile = f'profile of {request.user.user_profile}'
    return HttpResponse(profile)
Korn answered 31/1, 2021 at 6:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.