Update March 2013:
Django 1.5 includes a configurable custom user model that makes all this obsolete.
Extra fields for Users
Most of the Django projects I’ve worked on need to store information about each user in addition to the standard name and email address held by the
The old way: User Profiles
The solution in the past was to create a “user profile” model which is associated 1-to-1 with the user. Something like:
class UserProfile(models.Model): user = models.ForeignKey(User, unique=True, related_name='profile') timezone = models.CharField(max_length=50, default='Europe/London')
AUTH_PROFILE_MODULE = 'accounts.UserProfile'
profile = request.user.get_profile() print profile.timezone
It works ok, but it’s an extra database query for each request that uses the profile (it’s cached during the request so each call to
get_profile() is not a query). Also, the information about the user is stored in two separate models, so you need to display and update fields from both the
User and the
The new way: Model Inheritance
If you’re using trunk as of revision 7477 (26th April 2008), your model classes can inherit from an existing model class. Additional fields are stored in a separate table which is linked to the table of the base model. When you retrieve your model, the query uses a join to get the fields from it and the base model.
Inheriting from User
Instead of creating a User Profile class, why don’t we inherit from the normal
User class and add some fields?
from django.contrib.auth.models import User, UserManager class CustomUser(User): """User with app settings.""" timezone = models.CharField(max_length=50, default='Europe/London') # Use UserManager to get the create_user method, etc. objects = UserManager()
Now each instance of
CustomUser will have the normal
User fields and methods, as well as our additional fields and methods. Pretty handy, no?
UserManager as the default manager so that the standard methods are available. In particular, to create a user, we really want to say:
user = CustomUser.objects.create(...)
If we just created the user from the
User class, we wouldn’t get a row in the
CustomUser table. Creation needs to be done in the derived class.
You can still get and update the underlying
User model, no problem, but it won’t have the additional fields and methods found in our
CustomUser class by default
So far, there’s one problem. When you get
request.user, it’s an instance of
User, not an instance of
CustomUser, so you don’t get the extra fields and methods.
What we want is for Django to retrieve the
CustomUser instance transparently. It turns out to be quite easy.
Users come from authentication backends
The default authentication backend gets the
User model from the database, checks the password is correct then returns the
User. You can write your own authentication backend, for example to check the username and password against some other data source or to use the email address instead of username.
In our case, we can use an authentication backend to return an instance of
CustomUser instead of
the authentication backend in
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
AUTHENTICATION_BACKENDS = ( 'myproject.auth_backends.CustomUserModelBackend', ) ... CUSTOM_USER_MODEL = 'accounts.CustomUser'
That’s it. Now when you get
request.user, it’s an instance of the
CustomUser class with whatever additional fields or methods you have added.