How to extend django's user class and change authentication middleware.
Hi everyone,
Welcome for the first edition for python-friday ! This python friday will be dedicated to the so popular django web framework because I really have my hands in it for the time being.
Django offers a very nice User class to manage all sort of users, set rights, contact information and so on. But sometimes, you want to extend that class to add some extra information.
The problem:
You want to use your – let’s call it – Customer class as the central user class and authenticate using django’s user modules.
The solution:
Extend the user classs to add your data and configure django framework to use your new class as a middleware and authenticate on it.
Extending the class.
from django.contrib.auth.models import User, UserManager
class Customer(User):
"""
Model for customer.Hold administrative information
"""
company_name = models.CharField('company name', unique=True, max_length = 50)
tax_number = models.CharField('tax number', max_length = 12)
address = models.CharField('address line 1', max_length = 200)
zip_code = models.IntegerField('zip code', max_length = 10)
city_name = models.CharField('city name', max_length = 20)
country = models.CharField('country', max_length = 20)
edited_on = models.DateTimeField(auto_now=True, auto_now_add=True, editable=False)
# We setup a manager here.. not really sure this is useful
objects = UserManager()
def __unicode__(self):
return self.company_name
# In the save function, we implement our own password
# management. If the password is already hashed in the form
# we just dont change anything otherwise we call the set_password()
def save(self):
password = ""
r = re.compile('sha1\$.*')
if not r.match(self.password):
password = self.password
self.set_password(self.password)
User.save(self)
Now that we have extended the class, we can play a bit with it to see how things goes :
> c = Customer() > c.username = "mms" > c.lastname = "sauvage" > c.firstname = "manumanu" > c.company_name = "the Corp" #and so on ... and finally > c.save()
But wait …. Haven’t we said that django has its own authentication methods ? Well, how do you want Django framework to know that it has to authenticate against the customer table and NOT against django users’ table ?
Well, django can let us redefine the authentication backend. Doing so will then change the authentication behavior the way we want to be.
Redefining djjango’s authentication backend.
First we have to declare our new authenticate backend in the settings.py. While we are in that file, we will also tell django which class to use to authenticate :
AUTHENTICATION_BACKENDS = (
'project.auth_backend.CustomerModelBackend',
)
CUSTOM_USER_MODEL = 'accounts.Customer'
We just say here that the backend will be in the file auth_backen.py and the class will be named CustomerModelBackend.
Then create a file called auth-backend.py in your project directory and add it the following content:
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 CustomerModelBackend(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
The authentication backend is a class with 2 methods (get_user and authenticate). This is well explained in the documentation of django framework.
Now we have a Class for our Extended users, an authentication backend that is configured through our settings.py.
Yes ! This is what we want. The most lazy of you can stop here and start coding his favorite user management webapp.
Syncdb / admin problem.
But .. wait a minute. Do you remember when you do your first syncdb ? manage.py ask you if you want to fill in an administrator. If you say yes at that point, manage.py will create a django user and now that we authenticate on Custome’s table, there is a lot of chances that the admin module will not authenticate properly (for the admin only though …).
The solution is to modify the authentication backend to authenticate on django users’ table in case of a failed authentication on customer’s table.
def authenticate(self, username=None, password=None):
try:
user = self.user_class.objects.get(username=username)
except self.user_class.DoesNotExist:
try:
user = User.objects.get(username=username)
except User.DoesNotExist:
return None
As you can see, the code is pretty straight forward and self explanatory. The only tricky part is the “@property” but I let you dig into that yourself as you will learn a lot of things if you want to understand it completely.
Et voila !

2 Responses to “How to extend django's user class and change authentication middleware.”
Sat Mar 2010 at 8:35 pm
Thanks for this post. I am new at django and this will be a big help.
Wed May 2010 at 6:58 pm
Nice! Worked wonderfully.