There are basically four ways to do it:

  1. use get_profile() and an external class
  2. use a Proxy model
  3. subclass User
  4. monkey-patching

Let's see their pros/cons.

1. use get_profile()

This is what everybody should use. Before all, we have to define a class containing new methods/attributes. Let's say:

class UserProfile(models.Model):
    user = models.OneToOneField(User, unique=True)
    url = models.URLField()
    home_address = models.TextField()
    phone_number = models.PhoneNumberField()

Being a one-to-one relationship, we have to define a OneToOneField.

A new table, called user_profile, will be created when we sync the database, but we still have to catch the post_save signal in order to create a new UserProfile instance, associated to a single user.

def create_profile(sender, instance, created, **kwargs):
    if created:
        profile, created = UserProfile.\
                 objects.get_or_create(user=instance)
post_save.connect(create_profile, sender=User)

Eventually, we have to set the AUTH_PROFILE_MODULE constant inside settings.py (e.g, AUTH_PROFILE_MODULE=‘accounts.UserProfile’).

Pros:

  • Easy to implement.
  • Low coupling: User does not need UserProfile to exist.
  • Strong cohesion: we are not adding new responsabilities to User.

Cons

  • A new table is needed
  • An extra query each time we have to access to a UserProfile field:
user = User.objects.get(pk=1)
profile = user.get_profile()
print profile.home_address

2. Use a Proxy Model

Another general way to add new functionalities to our User class is by defining a Proxy Model. We are not going to extend User, just like in the previous method, because we are going to use the Proxy Pattern. Let's write a sample:

class ProxyUser(User):
    class Meta:
        proxy = True

def get_username_and_email_as_tuple(self):
    return self.username, self.email

Since they are sharing the same interface, our ProxyUser can access the User attributes/methods.

Do you want to sort the users by username?

class OrderedUser(User):
    class Meta:
        ordering = ["username"]
        proxy = True

However, we can still add a new manager:

class ProxyUserManager(models.Manager):
    ... # your methods here

class ProxyUser(User):
    objects = ProxyUserManager()
    class Meta:
        proxy = True

Pros:

  • Easy to implement
  • Low coupling
  • Using proxies, your application is more extendable with existing modules
  • You don't need 2 queries to get a User instance

Cons:

  • You cannot add new fields to User
  • You are tied to Proxies in order to use new functionalities

3. Subclass User

This is the most intuitive OOP method. However, as James Bennet explains here:

I’d wager that probably 90% or more of the things people say they want to do with subclasses could be better accomplished by instead defining a related model and linking it back with a unique foreign key.

He's basically suggesting to use get_profile(). Then he says:

I’ve seen a lot of people say they want to subclass User not because they want to change the types of auth-related information, but because they want to add a field for the user’s website URL, or a short “bio” field, or lots of other useful information related to the user.

Did you spot the key word in that last phrase? Other useful information related to the user. That should be a dead giveaway that what we want in the database is a separate table where each row relates back to a row in the auth table. And in OO terms, the user’s website, bio and other information aren’t really part of their authentication and access controls and really should be encapsulated in their own object. So in OO terms what we want is a separate class where each instance has an attribute pointing to an instance of User.

So the question: why do you want to add new responsibilities to User, when it was born to manage auth-related data, like username, email, password? If you want to change the authentication backend, well that's a good reason. Here I found some code we can use to understand how to do that:

from django.contrib.auth.models import User, UserManager

class CustomUser(User):
    timezone = models.CharField(max_length=50,
                                default='Europe/London')
objects = UserManager()

This subclass does not do anything useful: it simply adds a new field.

However, useless or not, we have to specify a new auth-backend.

from django.conf import settings
from django.contrib.auth.backends import ModelBackend
from django.core.exceptions import ImproperlyConfigured
from django.db.models import get_model

class CustomUserModelBackend(ModelBackend):
    def authenticate(self, username=None, password=None):
        try:
            user = self.\
                 user_class.objects.get(username=username)
            if user.check_password(password):
                return user
        except self.user_class.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return self.user_class.objects.get(pk=user_id)
        except self.user_class.DoesNotExist:
            return None
    @property
    def user_class(self):
        if not hasattr(self, '_user_class'):
            self._user_class = get_model(
            *settings.CUSTOM_USER_MODEL.split('.', 2))
            if not self._user_class:
                raise ImproperlyConfigured(
                          'Could not get custom user model')
        return self._user_class

Then, go open settings.py and tell Django that you want to use CustomUser as your new User model and CustomUserModelBackend as your auth-backend:

AUTHENTICATION_BACKENDS = (
'myproject.auth_backends.CustomUserModelBackend',
)

CUSTOM_USER_MODEL = 'accounts.CustomUser'

Pros:

  • The best way to add new auth-related fields.
  • Just one table for each entity.
  • Just one query to get a User instance.

Cons:

  • It may give problems: read some comments here.
  • If you are not an OOP guru, you will begin to add new not-auth-related fields, like "books read", "your pet's name" and so forth.

4. Monkey patching

Let's start by saying that you don't have to use this method. I am still astonished that I saw its usage inside a larger project, in particular here: askbot/models/__init__.py. You define a method (or an attribute), user_get_absolute_url(instance), and then it will be added to User at runtime by using the add_to_class method, defined by ModelBase

User.add_to_class('get_absolute_url', user_get_absolute_url)
User.add_to_class('get_profile_url', get_profile_url)
# altro codice
def user_get_absolute_url(self):
    return self.get_profile_url()
def get_profile_url(self):
    """Returns the URL for this User's profile."""
    return reverse('user_profile',
                   kwargs={
                           'id' : self.id,
                           'slug' : slugify(self.username)
                          }
                  )

Pros:

  • Easy to implement.
  • No extra queries.
  • No extra tables.

Cons:

Some words about Django 1.5

As you may see in the official documentation, the constant AUTH_PROFILE_MODULE is no longer supported. This means that the first method described in this post is not anymore a good way to add new functionalities/attributes. You should prefer an external class, e.g., UserProfile containing a OneToOneField to User:

from django.contrib.auth.models import User

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

>>> u = User.objects.get(username='fsmith')
>>> freds_department = u.employee.department

Of course, you still have to catch the post_save signal to associate a UserProfile instance to a User instance.

However, another way of implementing a new User authentication model has been implemented in Django 1.5. You first create a custom MyUser class, then set the constant AUTH_USER_MODEL = accounts.MyUser. Don't forget that throughout the code, you have to replace User with django.contrib.auth.get_user_model(), because now you have to reference to a different User model.

Useful links

#django #python