Looking for a Django developer? Red Robot Studios is now accepting clients for Django development projects. Find out more...

Extending the Django User model with inheritance

21 August 2008

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

57 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

RSS feed for comments on this post. TrackBack URL

Leave a comment