Extending the Django User model with inheritance

21 August 2008

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 contrib.auth.models.User model.

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:

the model

class UserProfile(models.Model):
    user = models.ForeignKey(User, unique=True, related_name='profile')
    timezone = models.CharField(max_length=50, default='Europe/London')

config in settings.py

AUTH_PROFILE_MODULE = 'accounts.UserProfile'

usage

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 UserProfile models.

The new way: Model Inheritance

As part of the great work done on the queryset-refactor by Malcolm et al, Django now has 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?

We add 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.

Getting the 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 User.

the authentication backend in auth_backends.py

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

config in settings.py

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.

P.S. Looking for Django hosting? I’d recommend WebFaction for shared hosting and Slicehost for a VPS.

Filed under: Django — Scott @ 8:40 pm

90 Comments »

  1. this is beautiful.

    Comment by Mogga — 21 August 2008 @ 9:16 pm

  2. Instead of using a custom authentication backend a middleware could be used to replace contrib.auth’s one, i think it would be more simple. Nice example.

    Comment by David Elias — 22 August 2008 @ 1:42 am

  3. Since inheritance is implemented under the hood as a one-to-one relation, this doesn’t reduce the number of queries at all — accessing User fields from your subclass will do a query, and accessing subclass fields from a User will do a query.

    Also, I *still* think this is the wrong way to go about it from a design perspective: http://www.b-list.org/weblog/2007/feb/20/about-model-subclassing/

    Comment by James Bennett — 22 August 2008 @ 3:33 am

  4. pretty easy for using the user model!
    thanks

    Comment by sean — 22 August 2008 @ 1:43 pm

  5. I think I agree with James. Also, the get_profile() is almost a convention on pluggable apps already.

    Comment by Eduardo Padoan — 22 August 2008 @ 7:55 pm

  6. @David Elias
    Using middleware sounds like a good idea.

    @James Bennett
    That’s not the behaviour I’m seeing in r8194. I’m seeing one query with an inner join when the model is retrieved, after which fields from both the base and subclass can be accessed without further queries. That’s one query, versus two queries for user + profile. Have I misunderstood you?

    >>> from accounts.models import CustomUser
    >>> c = CustomUser.objects.get(username='sgb')
    >>> print c.first_name
    Scott
    >>> print c.timezone
    Europe/London
    >>> from django.db import connection
    >>> connection.queries
    [{'time': '0.231', 'sql': 'SELECT "auth_user"."id", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."password", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."is_superuser", "auth_user"."last_login", "auth_user"."date_joined", "accounts_customuser"."user_ptr_id", "accounts_customuser"."timezone" FROM "accounts_customuser" INNER JOIN "auth_user" ON ("accounts_customuser"."user_ptr_id" = "auth_user"."id") WHERE "auth_user"."username" = \'sgb\' '}]

    @Eduardo Padoan
    What do the pluggable apps do with the profile? How do they know what fields it has? It’s an interesting point I hadn’t thought of.

    Comment by Scott — 24 August 2008 @ 8:32 pm

  7. I got everything modified, but had no luck getting my customuser to show up in the admin site. I’m just learning django while writing a web app, so I may be making too many assumptions. Should my customuser show up in the admin pages for users. It still looks like a user, not customuser.

    Comment by Nik — 1 September 2008 @ 1:24 am

  8. @Nik

    Your CustomUser class can show up in admin as well as the User class. You just need to register it in the usual way. For example, if you have a CustomUser model, in the admin.py file you could have:

    from django.contrib import admin
    
    from models import *
    
    admin.site.register(CustomUser)

    Or to set some display options:

    from django.contrib import admin
    
    from models import *
    
    class CustomUserAdmin(admin.ModelAdmin):
        list_display = ('first_name', 'last_name', 'timezone')
    admin.site.register(CustomUser, CustomUserAdmin)

    If you don’t want User to show up in admin, you can unregister it, with something like this in your main urls.py file:

    from django.contrib import admin
    from django.contrib.auth.models import User
    
    admin.autodiscover()
    admin.site.unregister(User)

    Comment by Scott — 1 September 2008 @ 9:43 am

  9. Hello, Scott

    i’ve tryed this example
    ——
    from django.contrib import admin
    from django.contrib.auth.models import User

    admin.autodiscover()
    admin.site.unregister(User)
    ——-
    but get in return only exceptions like

    Error while importing URLconf ‘MyProject.urls’: ‘module’ object has no attribute ‘site’

    Comment by Vovk — 4 September 2008 @ 10:13 am

  10. Hi,

    What if I need *multiple* custom users, e.g., teachers, students, and staff members? Creating multiple subclasses is straightforward, obviously, but what about the authentication backend? How, for different types of users, do I plug the right class into ‘user_class’? Or, more generally, return an instance of the appropriate subclass

    I’m a django newbie just trying to get this all figured out.

    Thanks in advance!

    Comment by Ed Hagen — 4 September 2008 @ 7:01 pm

  11. 2 Ed Hagen:

    I’m in the exact situation as you described. I use another approach: create abstract base model with extra fields, common to all profiles, then derive each profile model from this base. Thus, you’ll have additional foreign keys in the Django’s User model, pointing back to your profiles. It’s up to your application logic to decide whether the user is allowed to have multiple profiles of different types or only the one. The here mentioned middleware (or backend) could check, for example, if user.teacher is not None: return user.teacher and so on. Personally, I’m not using inheritance from Django User (because my users can have profiles of different types simultaneously), but you could try it ;)

    Here is sample code:

    class BaseProfile(models.Model):
        """Abstract model with site's profile data"""
    
        # Link to the Django user record
        user = models.OneToOneField(User, related_name='%(class)s',
            verbose_name=u'Django user', unique=True,)
    
    ... additional common fields goes here
    
        class Meta:
            abstract = True
    
    class Person(BaseProfile):
        """
    Site's personal profile for application-specific personal data.
    """
    
    ... Person specific fields goes here
    

    Each profile will have ‘user’ field:

    person = Person.objects.get(id=...)
    person.user

    In the Django User:

    user = User.objects.get(id=...)
    user.person

    You could have as many profile types as you want :)

    Comment by irium — 6 September 2008 @ 11:48 pm

  12. I haven’t yet tried your solution but I think you saved my life.

    In my app I made a “comments” app which allows users to comment some entities like News and other stuff. The problem is that I’m displaying, for each posted comment, the user’s avatar, which is part of its profile, which was fetched in a new SQL query for each comment. So I’m getting (comments_count) SQL queries executed, which is not a viable solution.

    I think your solution will solve this problem and I thank you for that.

    Comment by Sephi — 7 September 2008 @ 10:43 pm

  13. @Vovk

    Maybe you have an older version of Django. admin.site was added as part of the newforms-admin merge in r7967

    Comment by Scott — 13 September 2008 @ 8:52 pm

  14. I’m going through a trial implementation on a branch of a site I’m developing. But I’ve run into a stumbling block. Basically, it does not seem to play well with the django-registration module. Have you tried integrating it with that module?

    Comment by Paul — 19 September 2008 @ 5:53 pm

  15. I must be missing something.. how is the table for CustomUser created?

    –confused.

    Comment by Mikael — 3 October 2008 @ 8:28 am

  16. @Mikael

    The table is created when you run manage.py syncdb. The same as the tables for any other model.

    If you used the User model, then you changed to use a CustomUser model, the CustomUser instances won’t automatically exist. You will need to manually create them in the database and make them point to the rows in the User table. When you create a CustomUser instance in future, one row is inserted in the *_customuser table and one in the auth_user table.

    Comment by Scott — 3 October 2008 @ 9:28 am

  17. Any Chance of getting a middleware example I’m stumped

    Comment by Daniel Worth — 8 October 2008 @ 10:02 pm

  18. @Daniel

    If you have a look in the Django source in contrib/auth/middleware.py, you’ll see what it’s doing.

    I haven’t used it, but you could write a middleware function something like:

    from accounts.models import CustomUser
    
    class AuthenticationMiddleware(object):
        def process_request(self, request):
            request.__class__.user = CustomUser()
            return None

    Then in your settings.py instead of the usual:

    MIDDLEWARE_CLASSES = (
        ...
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        ...)

    You would have:

    MIDDLEWARE_CLASSES = (
        ...
        'myproject.middleware.AuthenticationMiddleware',
        ...)

    Hope this helps.

    Comment by Scott — 9 October 2008 @ 9:49 am

  19. @irium and @Scott

    I was really tired yesterday and should have been specific.
    The code example helps me understand middleware a little better. However here is the situation. The site needs to have a separate profile for male models, female models, stylists and photographers.
    My plan is to implement it as suggested by irium so I can have multiple profiles.
    My confusion is how to handle AUTH_PROFILE_MODULE = ?
    and what would the middle ware need to look like?
    Would a middleware separate from a new AuthenticationMiddleware be needed or is there something I’m missing in how profiles are being passed.
    I’m reading code like a mad man but for some reason haven’t been able to wrap my head around it yet.

    Comment by Daniel Worth — 9 October 2008 @ 3:43 pm

  20. Hello Scott, thanks for an informative article.

    I’m having some trouble, as I’m a django newb, first of all I have the feeling that new users created with the admin interface will not have entries created in the customuser table as well (although I hope I’m wrong on this one). Second I have some trouble creating new users from the shell. The reason for my modifications to the user model, is that I want to tie the users in to parts of my application model, not just add more metadata to each user. Currently when using the objects.create_user(username,email,pass) method, I get a database exception:
    IntegrityError: (1048, “Column ‘customer_id’ cannot be null”)

    Now in my models I’ve set up the following:

    class Customer(models.Model):
        name = models.CharField(max_length=100)
        website = models.URLField()
        def __str__(self):
            return self.name
        def __unicode__(self):
            return self.name
        class Admin:
            pass
    
    class CustomUser(User):
        """User with app settings."""
        timezone = models.CharField(max_length=50, default='Europe/London')
        customer = models.ForeignKey(Customer)
    
        # Use UserManager to get the create_user method, etc.
        objects = UserManager()

    So the problem is obviously the fact that the UserManager can’t create the customuser table entries, because the customer_id foreign key field there can’t be null.

    Now this is probably a complete newb question, but I’m wondering how I can overload the user creation so that when creating a new customuser I can pass along a customer to which it belongs?

    Best,
    Steinn E. Sigurðarson

    Comment by Steinn — 14 October 2008 @ 5:47 pm

  21. @Steinn

    You are correct that creating a User in admin will not cause a CustomUser to be created. You need to make it so CustomUsers are created in admin. See comment #8 above for an idea of how to do that.

    Does Customer need to be a separate model from CustomUser? If not, you could use Customer as your custom user class — that’s what I’m doing in my current project.

    If they do need to be separate, you could make the customer property of CustomUser optional so a CustomUser can be created first then a Customer can be created and assigned. That’s a bit messy, though.

    An alternative is to write your own manager for CustomUser and give it a create_user method that does the right thing. You can look at the source in django/contrib/auth/models.py to see what the default method does (not much), copy and customise.

    If there is always one Customer for each CustomUser, it seems like maybe they should be the same model, so that would be the first thing to consider.

    Comment by Scott — 15 October 2008 @ 12:46 am

  22. Thanks Scott, I’ll take a look at these links.

    Each customer is a company, and can have several users under it, so unfortunately I can’t go with the route of having the CustomUser be the Customer.

    One thing I realized yesterday after posting my question here, was that when I go into the administration I can go and create a CustomUser as part of my application, and that will of course create the entries in both tables (and allows me to choose which Customer the new user belongs to). Since all the users will be created this way, I think I can manage without writing a new manager :-)

    Best,
    Steinn

    Comment by Steinn — 15 October 2008 @ 12:59 pm

  23. I tried your example above, but when I try to login the user explicitly through login(request, user), I get the following error message:

    ‘CustomUser’ object has no attribute ‘backend’

    And the code is below:

    def my_view(request):   
        if request.method == 'POST': # If the form has been submitted...
            form = LoginForm(request.POST) # A form bound to the POST data
            if form.is_valid():
                email = request.POST['email']
                password = request.POST['password']
                c = CustomUserModelBackend()
                user = c.authenticate(email=email, password=password)
                if user is not None:
                    if user.is_active:
                        login(request, user)

    When I looked into the super authenticate method, it does set the backend. Not sure why it’s not being set in my case.

    Note: I am able to authenticate the user fine, but login fails with the given error message.
    My settings.py has following entry for AUTHENTICATION_BACKENDS:

    AUTHENTICATION_BACKENDS = (
        'learnsite.events.AuthBackends.CustomUserModelBackend',
    )

    Any insight will be appreciated.

    Comment by Ravi Hasija — 18 October 2008 @ 11:03 pm

  24. @Ravi

    I think instead of making an instance of CustomUserModelBackend and calling authenticate on it, you should just call the authenticate function which is in django.contrib.auth.

    If you look in the source you can see it calls the backend’s authenticate method and then sets the backend attribute on the user object.

    It uses the backend(s) configured in your settings.py, so it will be using the CustomUserModelBackend anyway.

    So, more like:

    from django.contrib.auth import authenticate
    ...
    user = authenticate(email=email, password=password)
    ...
    login(request, user)

    Comment by Scott — 18 October 2008 @ 11:22 pm

  25. Scott,

    Thanks a million. I did look in the source and saw that authenticate method is setting the backend. Coming from Java background, I thought the child method will call super() automatically and did not know that I should call the parent directly.

    It works! BEAUTIFUL! You made my day!

    Thanks again for the example.

    Comment by Ravi Hasija — 19 October 2008 @ 12:10 am

  26. Thanks! You’ve saved me a lot of time!
    Nice example!!

    Comment by Eduardo — 29 October 2008 @ 6:16 pm

  27. I do inheritance from User model, but i just leave the ‘django.contrib.auth.middleware.AuthenticationMiddleware’,
    Since i can get access to my custom user trough user i do:

    @login_required
    def my_view(request):
      try:
         cutom_user=request.user.custom_user
      except Custom_User.DoesNotExist:
         return HttpResponseRedirect(reverse('custom_client_login'))
    

    i would like to have your feedback :)

    Thanks in advance,

    Sergio Hinojosa

    Comment by sergio — 26 November 2008 @ 3:57 am

  28. @Sergio

    If you have to add those four lines to each view, that’s a fair bit of extra code, especially if you have lots of views. You could write a custom decorator to do that and set request.user or request.custom_user in the decorator.

    Another thing to consider is if you are making an extra call to the database when you get request.user.custom_user. The advantage to custom middleware or authentication backend is that you only need to hit the database once to get the custom user object.

    Comment by Scott — 26 November 2008 @ 11:03 am

  29. Hi guys, I found a way to create my custom User model every time a auth User instance is created. I putted these lines on my models.py:

    from django.db.models import signals
    
    def create_profile_for_user(sender, **kwargs):
        if kwargs['created']:
            p = Profile() # 'Profile' is my custom User model
            p.__dict__.update(kwargs['instance'].__dict__)
            p.save()
    
    signals.post_save.connect(create_profile_for_user, sender=User)

    I hope this helps someone else!

    – Thiago

    Comment by Thiago Alves — 1 December 2008 @ 5:25 pm

  30. The inheritance feature is great so far, but there is still a little error in the configuration.
    Registration to the admin should be done this way:

    [code]
    from django.contrib.auth.admin import UserAdmin
    admin.site.register(CustomUser,UserAdmin)
    [/code]

    Then, it’s also possible to set/change the password of the user

    Comment by bencoder — 23 December 2008 @ 11:51 am

  31. Hi Scott!
    I am trying to use the method you have described here to extend the User model, but I get some errors when I am trying to save the User. The error is:

    IntegrityError at /join/
    
    (1048, "Column 'age' cannot be null")
    
    Request Method: 	POST
    Request URL: 	http://localhost:8000/join/
    Exception Type: 	IntegrityError
    Exception Value: 	(1048, "Column 'age' cannot be null")
    

    My custom user model looks like this:

    class CustomUser(User):
        age = models.IntegerField()
        occupation = models.IntegerField()
        
        #Use UserManager to get the create_user method, etc.
        objects = UserManager()
    

    The view that tries to save the user looks like this:

    def join(request):
        """Creates a new user."""
        title= "Join"
        if request.method == 'POST':
            username = request.POST.get('username', '')
            password = request.POST.get('password', '')
            email = request.POST.get('email', '')
            user = CustomUser.objects.create_user(username, email, password);
            user.age = request.POST.get('age', '')
            user.occupation = request.POST.get('occupation', '')
            user.save()
            return HttpResponseRedirect('/')
        return render_to_response('new.html', locals())
    

    Can you please help me to find out what am I doing wrong?

    Thank you!

    Comment by Radu — 12 January 2009 @ 12:40 am

  32. Scott, This is very interesting. Thanks for writing it up. I look forward to trying it. Does this break any code (Django or third-party) that references User beyond Django Auth? Or do your tweaks mean that any app that references User is actually referencing CustomUser? I’d like to use Django Groups and other User-related code without having to worry about it breaking because of this approach.

    Generally, is there any reason NOT to do this versus the old CustomProfile approach? Thanks!

    Comment by mc3 — 13 January 2009 @ 10:53 pm

  33. @mc3

    If you use a custom authentication backend as I have, you should find the User is always a CustomUser. I haven’t tried it with any third-party apps so I can’t say if it breaks anything but I don’t see why it would. We’re using it at Red Robot and so far it’s working well.

    Comment by Scott — 14 January 2009 @ 10:23 am

  34. Scott,
    Thanks. I’m giving it a shot. I thought I had replciated all your steps correctly but I get this error.

    Seems to get hung up on this line:
    except self.user_class.DoesNotExist:

    Not sure how to get round it.

    Exception Value:
    ‘NoneType’ object has no attribute ‘DoesNotExist’
    Exception Location: /home/ldm616/webapps/django/fabproject/auth_backends.py in authenticate, line 12

    Comment by mc3 — 14 January 2009 @ 6:42 pm

  35. @mc3

    Check CUSTOM_USER_MODEL is set in your settings.py file. It sounds like the authentication backend is failing to get your model class (uses django.db.models.get_model).

    Comment by Scott — 14 January 2009 @ 6:54 pm

  36. I think I’ve a solution to matt’s and radu’s problem.
    Just use the CustomUser-Model like a normal model, save all needed data, like username, first_name, last_name, password etc.

    >>> new_user = CustomUser(username='newuser', first_name='firstname', last_name='lastname', password='pass'....)
    >>> new_user.save()

    !!ATTENTION!!
    After saving the new user, you’ve to set the encrypted password!

    >>> user = User.objects.get(username__exact='newuser')
    >>> user.set_password('pass')
    >>> user.save()

    It’s just a workaround, but it works fine here…

    Comment by metty — 16 January 2009 @ 12:33 pm

  37. @mc3
    I had that problem, and I changed CUSTOM_USER_MODEL from ‘accounts.CustomUser’ to ‘mysite.CustomUser’

    @metty
    Is there some way to have set_password called when creating the CustomUser from that Admin interface?

    Comment by Enj — 19 March 2009 @ 7:40 pm

  38. I have been encountering an error when I try to login to the admin interface. I am using a fresh database.

    FieldError at /admin/
    Cannot resolve keyword ‘username’ into field. Choices are: basic, id

    I am using the the original code in the post, which I think reading the comments should work in my case because I am essentially just adding one extra field to the users. Thanks for the help.

    Comment by Peter — 21 March 2009 @ 7:46 pm

  39. Hi Peter.

    Check your custom user model is inheriting from User not models.Model. Can’t think why else the username field would not be present.

    Comment by Scott — 22 March 2009 @ 2:25 pm

  40. I’ve tried this with the following issues/problems:
    1 — The autodiscover caused an error; so I deleted it.
    2 — When I login, it didn’t let me in until I changed my customuser database table to contain a “pointer” to my auth_user table entry (in this case, a 1).
    3 — Now, Site administration says: “You don’t have permission to edit anything.”

    Any suggestions?

    Thanks,

    Ken

    Comment by Ken Lerman — 1 May 2009 @ 6:03 pm

  41. Hey Scott,
    Thank you very much for such a informative article. This really helped.
    I have the same question as Enj had. (Comment no. 37)

    I got it to working till the point, where I can see my CustomUser Object in admin page.
    But, when I try to add user using admin site on CustomUser object. I get a plain django form, where password field is plain textbox.( with change password link beside)

    Is there any way, where I can add a user using admin site with CustomUser object? And It asks for username and password initially(like when I add a normal user with User object). And then adds a entry into User and CustomUser table.
    ( I tried many things but not much success,one them was to use django signals and handle post_save() event, Also tried playing around with UserAdmin object…but no success)

    Thanks,
    Sujit

    Comment by Sujit — 11 June 2009 @ 5:37 pm

  42. Hi Sujit.

    Sorry, I’m not adding users in admin, so I’m not sure what the problem would be. A couple of things you might look at:

    1. Is this a problem with User/CustomUser only, or do you get similar behaviour with other model inheritance? e.g. create one model then another that inherits from it and see what model instance gets created in admin.

    2. Does it make a difference what manager CustomUser has? i.e. if it doesn’t have User’s manager.

    3. Could you work around it using a ModelAdmin for CustomUser?

    Good luck.

    Scott

    Comment by Scott — 12 June 2009 @ 9:40 am

  43. Hello Scott,
    Thank you very much for your reply. I did try many other options but things didn’t quite work according to my requirement. However, I want to use the model inheritance in admin site. Below is the thing I want to achieve. Kindly provide your advise of the same.

    1. In my project, There are two types of users, namely Controller and WebUser.
    class Controller(User):
    #more controller related fields
    pass
    class WebUser(User):
    #more webuser related fields
    pass
    2. In admin site I register these two classes
    admin.site.register(Controller)
    admin.site.register(WebUser)

    3. I want to add and delete Controllers and WebUsers.
    And don’t really want to manage User Objects. Requirement is to keep the semantics for the users
    clean. As controllers are devices which logs in periodically and do some stuff and Webusers are
    normal human users which interact with the system.

    Problems:
    1. I do get the Add form for both Controller and WebUser joined with retrospective User object.
    The problem is in password field is displayed as plain text( when saved saves it in plain text as opposed to with save_password() method).
    2. Also, “change password” link doesn’t work as well

    Questions:
    1. What is the best method to achieve something like above?
    2. Any pointers on the similar topics. I have gone through most of the threads available but to no avail.

    Thanks,
    Sujit

    Comment by Sujit — 6 July 2009 @ 5:19 pm

  44. @Sujit

    Sorry, this is way outside of anything I’ve tried, so I don’t have any advice to offer.

    Comment by Scott — 6 July 2009 @ 7:35 pm

  45. […] 20:39 Прочитал тут о расширении модели User с помощью наследования. Решил […]

    Pingback by Расширение модели User — 8 July 2009 @ 5:39 pm

  46. NameError: name ‘models’ is not defined

    When I extend the User object in version 1.0.2 of django using the instructions provided in this post, I get this error.

    my code is simple:

    from django.contrib.auth.models import User

    class TestUser(User):
    foo=models.CharField(max_length=40)

    I have a feeling that Django Version 1.1’s Proxy Class definition will address this issue, but I wanted to check with your community to see if this is a common problem.

    Comment by bartosz — 22 July 2009 @ 9:23 pm

  47. @bartosz

    Do you also have from django.db import models ? It looks like you are using models.CharField, without importing models.

    Comment by Scott — 23 July 2009 @ 10:33 am

  48. This idea is actually great. Thanks, and I think it would be nice to promote this method as much as possible.

    Yet I failed to apply this method successfully.
    First, the changes I’ve made:

    — Added to settings.py bottom:

    AUTHENTICATION_BACKENDS = (
        'website.auth_backends.CustomUserModelBackend',
    )
    
    CUSTOM_USER_MODEL = 'profiles.CustomUser'
    

    — Added to urls.py top:

    from django.conf.urls.defaults import *
    from django.contrib.auth.models import User
    from django.contrib import admin
    admin.autodiscover()
    admin.site.unregister(User)
    

    — settings.py INSTALLED_APPS:

    (    'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.sites',
        'django.contrib.admin',
        'fullhistory',
        'website.courses',
        'website.profiles' )
    

    — profiles/models.py:

    from django.db import models
    from django.contrib.auth.models import User, UserManager
    from fullhistory import register_model #this has nothing to do with users, it just logs every write to the database
    from website.courses.models import Course #website is my Django project folder obviously
    
    class CustomUser(User):
        cellphone = models.CharField(max_length=20)
        courses = models.ForeignKey(Course, null=True, blank=True)
        updatedat = models.DateTimeField(null=True, blank=True)
    
        objects = UserManager()
    

    I’ve tried with and without AUTH_PROFILE_MODULE = ‘profiles.CustomUser’, but with no luck.

    Any suggestions?

    Forgot to add, it gives me 403 CSRF.

    Comment by Necmettin Begiter — 26 July 2009 @ 4:23 pm

  49. @Necmettin Begiter

    If the 403 CSRF error is the problem you are having I suggest you remove django.contrib.csrf.middleware.CsrfMiddleware from your MIDDLEWARE_CLASSES and find out what the problem really is.

    Comment by Scott — 26 July 2009 @ 9:28 pm

  50. Hi all.
    I tried to create a form from CustomUser model. Everything is fine except that fields from customUser model can’t be saved.
    Sorry for my bad English

    Comment by Masarliev — 11 August 2009 @ 6:07 pm

  51. I just wanted to say thank you. This is an elegant solution to a common problem.

    Comment by Jason — 9 September 2009 @ 7:44 am

  52. Hacked this a bit to allow the backend to assign a user subclass based on the user’s group attribute.

    http://steps.ucdavis.edu/People/jbremson/extending-the-user-model-in-django

    Comment by Joel — 9 September 2009 @ 9:30 pm

  53. $ python manage.py createsuperuser does not ‘obey’ this configuration it stills create the user in User model. Is that a support lack or is there something I’m doing wrong?

    Comment by MarioGonzalez — 1 October 2009 @ 11:43 pm

  54. I found a hack to fix the createsuperuser and other problems when one creates a user instead of the custom user. Just use a sql trigger(I use mysql). Something like

    delimiter //
    create trigger trigger_custom_user
    after insert on auth_user
    for each row
    begin
    insert into myapp_customuser (user_ptr_id) values(NEW.id);
    //

    It’s at the sql level and therefore not ideal, but it works.

    Comment by Bufke — 28 November 2009 @ 1:37 am

  55. […] other things we wanted, so it made sense for us to use it. The next question is whether we try the inheritance approach or do we treat our legacy users table as a sort of User Profile and utilize the User module using […]

    Pingback by Django: Model Inheritance or Related Tables wrt AMO at Spindrop — 15 December 2009 @ 9:12 pm

  56. I have found one problem with apply this new extension to the User class. Maybe there is a simple solution to it..

    When I create a foreign key to the new CustomUser class and specify “to_field = ‘username'” it works fine with validation and saving but the moment you try and do a order_by on the new model which references a field in the User model it fails with customuser__username field not found.

    Example:

    class CustomUser(User):
        ......
    
    class X(models.Model):
        added_by = models.ForeignKey(CustomUser, to_field = 'username')
    
    X.objects.all().order_by('added_by__last_name)
    

    I use auto complete fields in my forms that use the username as a value so if I don’t specify to_field in my model the validation will fail as it’s expecting the CustomUser.id.

    Maybe there is a way to tell the ModelForm to validate using the username?

    Comment by Karol Joc — 21 December 2009 @ 12:42 pm

  57. Hi,

    thank you for this article.

    Unfortunately i’m having an issue; i’m able to create a CustomUser through the admin interface (like described in comment #8).
    But the CustomUsers will appears in admin/auth/user/ (without the custom fields) and nothing will appear in admin/myapp/customuser/.

    Is there a clean way to fully add/list/edit extended users through the admin interface or must i make my own admin interface?

    Comment by bufh — 20 January 2010 @ 5:12 pm

  58. […] 0 0 Хочу узнать как лучше регистрировать пользователей на сайте, которым будет закрыт доступ к админке, но с использованием джанговской идентификации.Есть такое приложение — django-registration http://code.google.com/p/django-registration/. Оно предоставляет все необходимое для регистрации пользователей с активацией аккаунта через почту.Все зарегистрированные пользователи будут вперемешку с администраторами в списке ПользователиВ админке есть фильтры =) Поставьте фильтрацию по «Статусу персонала».Модель User имеет ограниченный набор полейДа, можно воспользоваться расширением через UserProfile, а некоторые наследуют от стандартного User свой класс — http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/. […]

    Pingback by Регистрация и идентификация пользователей — 8 April 2010 @ 7:46 am

  59. […] Original Source […]

    Pingback by Extending the Django User model < WebIT.ca — 6 May 2010 @ 3:33 am

  60. Hi Scott,

    thanx for the solution – it is exactly what i was seraching for – but somehow it is not working.

    When trying to log in the admin site for example, i get the

    "ImproperlyConfigured at /admin/
    
    Module "mysite.auth_backends" does not define a "CustomUserModelBackend" authentication backend
    
    

    The auth_backends.py is exactly the same you showed, and the CustomUser is just different in it’s fields (not timezone for example)

    My Settings is configured like this:

    AUTH_PROFILE_MODULE = 'mysite..user_ext'
    
    AUTHENTICATION_BACKENDS = (
        'mysite.auth_backends.CustomUserModelBackend',
    )
    

    Any ideas? I want to feel the magic pony ;)

    Regards,
    Kalle

    Comment by Kalle — 16 May 2010 @ 8:01 pm

  61. […] already seen a few ways to do it, but can't decide on which one is the […]

    Pingback by Extending the User model with custom fields in Django — HTMLCoderHelper.com — 14 November 2010 @ 11:46 am

  62. Huge thanks! Very useful! I struggled a bit because at first I was calling an Abstract class as for my UserProfile.. but once I figured it out and replaced it by a proper class, your code worked beautifully!

    Comment by Cocobuster — 29 November 2010 @ 7:26 am

  63. Hallo,

    can someone tell me the easiest way to get the Groups tol return my custom user in their user_set?

    I want do do something like that:

    u_set = Group.objects.get(name=’Groupname’).customuser_set.all()

    instead of

    u_set = Group.objects.get(name=’Groupname’).user_set.all()

    Can someone help me?

    Comment by Josch — 18 December 2010 @ 9:22 pm

  64. @Josch

    Off the top of my head, you could get custom users for a group with something like:

    group = Group.objects.get(name=’Groupname’)
    u_set = CustomUser.objects.filter(groups__contains=group)

    Comment by Scott — 18 December 2010 @ 10:38 pm

  65. […] 我已经看到了一些 办法来做到这一点,但不能决定哪一个是最好的。 This entry was posted in 技术难题 and tagged […]

    Pingback by 用自定义的字段在Django的用户模型 | IT技术问答论坛IT技术问答论坛 — 10 February 2011 @ 7:07 pm

  66. […] would like each shop to be able to login to my application. Following as best I can the guide at http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/ and various other googlings, I’ve got part of the way there, but I’ve run into a […]

    Pingback by Creating Custom User Backends in Django | Coding Answers — 25 February 2011 @ 5:04 pm

  67. This looks like a great way to extend the User model class. As your original article was way back in 2008, I’m just wondering if any key issues have come to light over that time -esp with new releases of django – which have altered how you feel about this solution? For my part, I think this is a very elegant solution.

    Comment by Michael — 4 April 2011 @ 12:17 am

  68. @Michael

    I don’t recall any changes in django releases that caused problems. It works and I have used it in some projects, though I still use the ‘old way’ of user profiles as well. Inheriting from the User model can be slightly more awkward in admin. It depends what you’re doing – there’s definitely scope for both approaches.

    Comment by Scott — 4 April 2011 @ 10:46 am

  69. Hello Scott – thanks for quick the reply. By the way, I had a look at your company site Red Robot – very impressive stuff you’re doing. I’m going to experiment with your technique and see how it pans out. Really appreciate that people like you are supportive and open with their knowledge.

    Comment by Michael — 4 April 2011 @ 11:42 am

  70. Very good your article, congratulations to you and your work.

    But I have a doubt.

    Below is the code for you to see and error message.

    Thank you very much if you can help me.

    Thanks!

    models.py

    class MeuUser(User):
        """ stores customer order information used with the last order placed; can be attached to the checkout order form
        as a convenience to registered customers who have placed an order in the past.
        
        """
        cpf = personalized_field.CPFField()
        telefone = personalized_field.TelefoneField() 
        
        objects=UserManager()
    

    forms.py

    class FormularioRegistro(forms.Form):
        nome = forms.CharField(label=u'Nome')
        sobrenome = forms.CharField(label=u'Sobrenome')
        email = forms.EmailField(label=u'Email')
        senha1 = forms.CharField(label=u'Senha',widget=forms.PasswordInput())
        senha2 = forms.CharField(label=u'Senha (Novamente)',widget=forms.PasswordInput())
        cpf = campo_personalizado.CPFField(label=u"Digite o seu CPF")
        telefone = campo_personalizado.TelefoneField(label=u"Digite o Telfone") 
    

    views.py

    if request.method == 'POST':
            postdata = request.POST.copy()
            form = FormularioRegistro(postdata)
            if form.is_valid():
    	    username = request.POST.get('email', '')
                password = request.POST.get('senha1', '')
                email = request.POST.get('email', '')
                user = MeuUser.objects.create_user(username, email, password);
                user.cpf = request.POST.get('cpf', '')
                user.telefone = request.POST.get('telefone', '')
                user.save()
                return HttpResponseRedirect('/')
                novo_usuario = authenticate(username=user.email, password=cd.get('senha'))
    

    Error

    Cannot resolve keyword ‘cpf’ into field. Choices are: _message_set, cupon_adquirido, date_joined, email, first_name, groups, id, is_active, is_staff, is_superuser, last_login, last_name, logentry, meuuser, password, user_permissions, username

    Comment by jhoni — 29 April 2011 @ 8:49 pm

  71. @jhoni

    Strange, it works ok for me. Try print type(user) after you call create_user and check if it is an MeuUser instance.

    Comment by Scott — 29 April 2011 @ 10:27 pm

  72. […] 0 0 http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/ нашёл такой пост. На сколько оно актуально в […]

    Pingback by AUTH_PROFILE_MODULE — 30 April 2011 @ 7:27 pm

  73. Scott, you are right, i’am wrong in other details about my code.

    Once on more thank you very much,

    []’s

    Comment by jhoni — 2 May 2011 @ 1:50 pm

  74. Hi all!
    As the user comment 36, i got the problem with the passwords. They are not encryptated, so i had to override the save method.
    Btw, i´m adding users only in admin interface, so, i´ve created a defaultAdmin to this.

    In admin.py:

    class defaultAdminModel(admin.ModelAdmin):
    	def save_model(self, request, obj, form, change):
    		obj.set_password(obj.password)
    		obj.save()
    
    Now, the others admin classes: 
    class AdminLocaisAtendimento(defaultAdminModel):
    

    Thanks ! ;)

    Comment by Fred Chevitarese — 28 June 2011 @ 2:20 pm

  75. […] Outro artigo muito bom (inglês) é este aqui. http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance […]

    Pingback by Extendendo User do Django « Fred Chevitarese … — 28 June 2011 @ 2:31 pm

  76. […] already seen a few ways to do it, but can’t decide on which one is the […]

    Pingback by Extending the User model with custom fields in Django - Programmers Goodies — 4 July 2011 @ 6:46 pm

  77. I have used the inheritence method to build a CustomUser class. Apparently its working absolutely fine but when I create a new user from admin, its password is not being encoded and as a result, I am unable to login with the newly created user. However, login action do not produce any error on the screen but the user is not being redirected to its home page. When I try to update the user from admin site. the password is being displayed in plain text and not in the encded format.

    thanks in advance for any help. I am really stuck!!!

    Comment by Ammara — 18 July 2011 @ 3:20 am

  78. Ammara – see this post for a variation that deals with the Pasword not being encrypted and also the admin user not using this class but instead the original User.
    http://www.kolios.dk/2010/01/22/how-to-extend-django-user-class-and-change-authentication-middleware/
    Also see here for a more elegant solution for User override using User classname instead of CustomUser and Admin fix.
    http://nigel.jp/2011/06/django-user-authentication-and-extending-the-user-model/

    However you need to mix all three approaches to solve the problems in any one of them.
    Would be great if this post was updated (or a new post replacing it) to reflect the best way to do all of this… (IMHO)
    Thanks Scott for getting this started… Please consider doing an update and straighteneing all the wiggles out…

    Comment by mark — 29 July 2011 @ 11:30 am

  79. […] codice l’ho rubacchiato da un blog. from django.contrib.auth.models import User, UserManager class CustomUser(User):   […]

    Pingback by Come estendere la classe User in Django — 9 August 2011 @ 4:22 pm

  80. […] referencia y tienen algunos de estos ejemplos: Documentación en DJango para múltiples sitios Extending the django user model with inheritance Doing more with the Django admin Add a button to Django admin to login as a user […]

    Pingback by Django con Admin MultiSite « developer.cl — 26 September 2011 @ 5:04 am

  81. Hi Scott, liked the inheritance but can anyone suggest how I can make an attribute of the child model required, if it is inheriting the User class without saving the user if the instance of the child model is not saved? eg.

    class Customer(User):
    organization = models.CharField(max_length=80, unique = True)
    address = models.CharField(max_length=80)
    .
    ..
    objects = UserManager()

    If in the admin.py, model Customer is registered, on execution, we get the user creation form, with password after saving it, we exit from the module. We are able to see that the user exists in the django Auth, even if the Customer is not yet created. How do I override the save of the User class. Also I need to create other users for the application the normal way. Please suggest

    Comment by mghs — 30 December 2011 @ 10:30 am

  82. i get this error when i try to access my version of ‘CustomUser’ model on the admin site(i have shortened it for clarity):
    no such column: app_name_publicprofile.user_ptr_id.
    Here is my model:

    class PublicProfile(User):
        sex = models.CharField(max_length=100,choices=settings.SEX_CHOICES,
    verbose_name='sex')
        phone_num = models.IntegerField(verbose_name='phone number')
        id_num = models.IntegerField(verbose_name='id number')
        age = models.DateField(verbose_name='date of birth')
        occupation = models.CharField(max_length= 20,
    verbose_name='occupation')
        picture = models.ImageField(upload_to ='user_profile_images',
    verbose_name='user photo')
    
        objects = UserManager()
    

    but i can add users in the admin site.

    Any ideas?

    Comment by Mwai — 16 January 2012 @ 8:22 pm

  83. So, old post, but I found it so this might be helpful for someone. Similar to #74, but if you are saving your Profile outside of the admin you can override your CustomUser’s save class. It could look something like this:

    def save(self, *args, **kwargs):
        self.set_password(self.password)
        super(CustomUser, self).save(*args, **kwargs)
    

    https://docs.djangoproject.com/en/dev/topics/db/models/#overriding-model-methods

    Comment by Andy — 28 March 2012 @ 6:19 am

  84. I have a solution for those of you who have not been able to create super users with manage.py (either through syncdb or createsuperuser):

    Add this near your CustomUser class in models.py:

    def create_custom_user(sender, instance, created, **kwargs):
    	if created:
    		new_user = CustomUser(user_ptr_id=instance.id)
    		new_user.__dict__.update(instance.__dict__)
    		new_user.save()
    post_save.connect(create_custom_user, sender=User)
    

    Whenever a user is created it will also create an entry in the CustomUser table of the database.

    Comment by Dylan — 16 May 2012 @ 9:44 pm

  85. Thanks for the article, but I was stumped by something for a while and I thought I should share the solution.

    I had made a custom user that logged in via email instead of username, and I had the login working fine for my own view that I controlled, but the admin site would not let me log in anymore. I assume it was because it was trying to use my custom authentication backend to try and log into the admin site, but that doesn’t make sense because it asks for a username and password, not a email and password like how my custom authentication backend worked.

    Therefore, I had to add ‘django.contrib.auth.backends.ModelBackend’ to the end of my AUTHENTICATION_BACKENDS (under my custom backend) so that when my custom authentication failed, it would have the default django method to fall back on.

    If any one has a more elegant solution to the problem I had, I’d be happy to hear it.

    Comment by Tyler — 5 June 2012 @ 6:39 pm

  86. This seems to work fine.

    However, i cannot get the field “timezone” to appear when I am editing a CustomUser.

    It will appear in the display_list fine.

    But not in the edit page for a Custom User.

    If i define a fieldset to include it eg:

        fieldsets = (
           (None, {'fields': ('username', 'password',)}),
           (('Personal info'), {'fields': ('first_name', 'last_name', 'email', 'timezone')}),
        )
    

    It throws an error saying “invalid syntax” on the line where I have included “timezone”).

    Any ideas?

    Comment by Mark — 16 August 2012 @ 3:42 pm

  87. […] extending the user model according to this. […]

    Pingback by Django: how to store subdomain-based authentication usernames? — 17 August 2012 @ 3:15 pm

  88. […] already seen a few ways to do it, but can’t decide on which one is the […]

    Pingback by Extending the User model with custom fields in Django — 29 September 2012 @ 1:55 am

  89. Hello Scott,

    I am working on a project where a User can be associated to either one profile or the other (individual || corporate). At a loss, I must confess.

    This is what I have (on a file basis):

    == settings.py
    
    AUTH_PROFILE_MODULE = 'profile.Profile'
    
    == profile/models.py
    
    class Profile(models.Model):
     user = models.OneToOneField(User)
     class Meta:
      abstract = True
    
    class Individual(Profile):
     # specific fields
     class Meta:
      db_table = 'individual'
    
    class Company(Profile):
     # specific fields
      class Meta:
      db_table = 'company'
    

    Which creates two separate tables, both pointing at the auth_user table. Fine.

    == forms.py
    class IndividualForm(forms.ModelForm):
     model = IndividualProfile
     fields = () # list of fields to display
    
    == views.py (for individuals only - same for companies)
    userform = UserForm(data = request.POST)
    profileform = IndividualForm(data = request.POST)
    if userform.is_valid() and profileform.is_valid():
     [...]
     user.save()
     profile = user.get_profile()
    

    Now the whole thing works – up to a point. Django throws the following:

      SiteProfileNotAvailable at /.../
      Unable to load the profile model, check AUTH_PROFILE_MODULE in your project settings
    

    The user is created. The profile is too – but in both the profile tables (individual AND company).

    Any idea?

    Many thanks!

    Comment by Plop — 22 October 2012 @ 11:13 pm

  90. @Plop
    I think you’ll need to override get_profile() or use some other way of getting the right type of profile (e.g. get user profile if it exists else get company profile). Not sure why both profiles are being created.

    Comment by Scott — 23 October 2012 @ 11:04 am

RSS feed for comments on this post.

Leave a comment