Using Configurable User Models in Django 1.5

With the upcoming release of Django 1.5 one of the largest changes is that you can now specify your own User model. If you’re fine with Django’s current User model than you don’t have to change any code. If you want to take advantage of this new functionality then keep on reading as I’ll go through how to migrate your current application to the new configurable user model.

Getting Started

For the sake of simplicity let’s make our own User object that is exactly the same as Django’s current but fixes the email max_length field to comply with RFC 5321 of 254 characters and adds a required field for the user’s twitter handle.

# myapp.models.py 
from django.contrib.auth.models import AbstractBaseUser
 
class MyUser(AbstractBaseUser):
    username = models.CharField(max_length=40, unique=True, db_index=True)
    email = models.EmailField(max_length=254, unique=True)
    twitter_handle = models.CharField(max_length=255)
    ...
 
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['twitter_handle']

You’ll notice a few new things about this:

Inheriting AbstractBaseUser:
This adds a few helper fields such as password and last_login as well as methods for setting the users password, getting the username, checking if the user is active. You can checkout a full list of what it includes here.

USERNAME_FIELD:
This is a string describing the name of the field on the User model that is used as the unique identifier. This will usually be a username of some kind, but it can also be an email address, or any other unique identifier.

REQUIRED_FIELDS:
A list of the field names that must be provided when creating a user. REQUIRED_FIELDS must contain all required fields on your User model, but should not contain the USERNAME_FIELD.

Now that you’ve created your User model you have to tell Django that you want to use it instead of their default User model. To do this you add the following to your settings file:

# settings.py
AUTH_USER_MODEL = 'myapp.MyUser'

With this setting Django now knows which User model to use.

Using Foreign Keys

Once you’ve set up your model it’s now time reference it in other models.

from django.conf import settings
from django.db import models
 
class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL)

This tells Django to create a ForeignKey to the User model that you specify in your settings.

Custom Manager

Now that you’ve created your own User model you also need to create your own Manager to handle the creation of Users and Super Users. If your User model defines the same fields as Django’s default User you can just install Django’s UserManager.

from django.contrib.auth.models import UserManager, AbstractBaseUser
 
class MyUser(AbstractBaseUser):
    ...
    objects = UserManager

If your User model includes different fields you’ll need to define your own custom manager that extends BaseUserManager.

from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
 
class MyUserManager(BaseUserManager):
    def create_user(self, email, twitter_handle, password=None):
        if not email:
            raise ValueError('Users must have an email address')
 
        user = self.model(
            email=MyUserManager.normalize_email(email),
            twitter_handle=twitter_handle,
        )
 
        user.set_password(password)
        user.save(using=self._db)
        return user
 
    def create_superuser(self, email, twitter_handle, password):
        user = self.create_user(email,
            password=password,
            twitter_handle=twitter_handle
        )
        user.is_admin = True
        user.save(using=self._db)
        return user
 
 
class MyUser(AbstractBaseUser):
    ...
    objects = MyUserManager

Other Methods

There are a few other methods you need to include:

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, but it can be any string that identifies the user in an informal way.

is_active:
A boolean attribute that indicates whether the user is considered “active”. This attribute is provided as an attribute on AbstractBaseUser defaulting to True.

Final Example

# models.py
from django.conf import settings
from django.db import models
from django.contrib.auth.models import BaseUserManager, AbstractBaseUser
 
class MyUserManager(BaseUserManager):
    def create_user(self, email, twitter_handle, password=None):
        if not email:
            raise ValueError('Users must have an email address')
 
        user = self.model(
            email=MyUserManager.normalize_email(email),
            twitter_handle=twitter_handle,
        )
 
        user.set_password(password)
        user.save(using=self._db)
        return user
 
    def create_superuser(self, email, twitter_handle, password):
        user = self.create_user(email,
            password=password,
            twitter_handle=twitter_handle,
        )
        user.is_admin = True
        user.save(using=self._db)
        return user
 
 
class MyUser(AbstractBaseUser):
    email = models.EmailField(max_length=254, unique=True, db_index=True)
    twitter_handle = models.CharField(max_length=255)
 
    is_active = models.BooleanField(default=True)
    is_admin = models.BooleanField(default=False)
 
    objects = MyUserManager()
 
    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = ['twitter_handle']
 
    def get_full_name(self):
        # For this case we return email. Could also be User.first_name User.last_name if you have these fields
        return self.email
 
    def get_short_name(self):
        # For this case we return email. Could also be User.first_name if you have this field
        return self.email
 
    def __unicode__(self):
        return self.email
 
    def has_perm(self, perm, obj=None):
        # Handle whether the user has a specific permission?"
        return True
 
    def has_module_perms(self, app_label):
        # Handle whether the user has permissions to view the app `app_label`?"
        return True
 
    @property
    def is_staff(self):
        # Handle whether the user is a member of staff?"
        return self.is_admin
 
 
class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL)
 
 
#views.py
from django.contrib.auth import get_user_model
...
User = get_user_model()

Also read...

  • edwardo

    Thanks for your post! Please more django article!

  • vahid

    A nice post on a critical aspect of Django 1.5

  • Piotr

    class Post(models.Model):
    author = models.ForeignKey(settings.AUTH_USER_MODEL)

    I suppose that you could use also this model
    “appname.ModelName”

    example:
    author = models.ForeignKey(“accounts.MyUser”)

    • Adam McKerlie

      You could definitely do that. The problem with doing it that way vs the settings.AUTH_USER_MODEL way is that if you change the User model you’re using you’d have to go through all of your code and change it as well instead of changing it once in your settings. Both ways are valid though.

      • http://siciarz.net zsiciarz

        Another important reason is that authors of third-party Django apps striving for flexibility should possibly use the settings.AUTH_USER_MODEL value every time they need to refer to a concept of “user model”. If the app refers to a hardcoded ‘auth.User’ or even User imported from django.contrib.auth.models, then it obviously cannot know about my customized AwesomeUser class. But in the case of using settings.AUTH_USER_MODEL in model definitions, as long as third-party code uses common user attributes (such as username, email, permission checks), it will play nice with auth.User and custom user classes. Duck typing for the win! :-)

    • David

      What about…

      somerel = models.ForeignKey(auth.get_model_user())

      ?

  • http://cecinestpasun.com Russell Keith-Magee

    A nice post, but a quick correction — the maximum allowed length for an RFC compliant email address is 254, not 256.

    http://www.rfc-editor.org/errata_search.php?rfc=3696&eid=1690
    http://stackoverflow.com/questions/386294/maximum-length-of-a-valid-email-address

    • Adam McKerlie

      I mis-read the RFC thinking it was 256. The post is now updated.

  • Kerr

    Customized user models is the one thing that has kept me from porting a large PHP project over to Django. I’m so happy to see this feature in 1.5! Thanks for the succinct example.

    I see a very minor typo in the “Custom Manager” code block:

    twitter_handel=twitter_handle

    should be

    twitter_handle=twitter_handle

    I look forward to more 1.5 news.

    • Adam McKerlie

      Fixed :D

  • Greg

    Excellent post.

    Are there any problems with subclassing the custom user model to implement different user types?

    class Teacher(MyUser):
    # Teacher specific fields
    # …

    class Student(MyUser):
    # Student specific fields
    # …

    Or is there a better way to achieve this?

    • Klemen Šavli

      Same question here. Any ideas how this works out?

  • Tim Baxter

    I think it’s much smarter, especially if you need backward compatibility, to set a UserModel var to the results of either get_user_model() or auth.user, whichever comes first, then use your UserModel var as a FK as you normally would.

  • saad

    hey this is an awesome article. could you do a post where you could show how you would leverage this new User model to make hierarchical profiles. for example if there is a school app where a student, teacher and principal can login and they have different profiles and permissions and entirely different page

    • http://procrastinatingdev.com silent1mezzo

      I think still the best way to handle this would be to have a separate User Profile that has a M2M to the User object. You could inherit off a base User Profile model and then have the unique Student, Teacher and Principal attributes defined on their User Profile.

  • scottwillman

    I’ve implemented your example but I’m curious as to why inheriting BaseUserManager doesn’t bring along email address verification and password validation? I can easily make a new user with email: “foo” and password: “”. Where would this validation get implemented if it’s my responsibility?

    • scottwillman

      Nevermind! duh, this happens in the form validation…

  • Anderson

    Hi man I add permission to this Class?

    has_perm always returning true everyone has access.

  • Ben Roberts

    Nice post. But I was hoping you’d be showing me how to migrate my existing table of auth.Users into a table for my User model!

  • Jeremy Karnowski

    How do you display the new users on the generic admin page?

  • Maurizio Mariani

    My admin doesn’t work with mi custom user model, fails to authenticate te user I create when run syncdb.

    model.py https://github.com/ilGuccio/spark/blob/master/sparkdb/models.py

    admin.py https://github.com/ilGuccio/spark/blob/master/sparkdb/admin.py

    Where is the problem? :(

  • Antonio

    Thanks, it is very helpful and descriptive. I have a problem with the foreign keys. In my case I have to create two kind of users: Customers and Servicer Providers. How in this case can I pass a foreign key or a m2m key to a Service??