1

I know this question has been asked hundreds of times, but most of them contain -accepted- answers that are not valid anymore. Some of them are for Django 1.5, some of them are even older.

So I'm looking for an up-to-date answer. The question that most resembles my problem is this one.

I'm using the django-registration module and I want users to have additional fields (like integer user.points). I'm OK with implementing my own MyUser model and changing the rest of my program accordingly. However I doubt this will work in compliance with the registration module. So I guess the ideal way would be keep using the User model, and somehow tie them so that whenever a User object is created, a corresponding MyUser object is created with the default values for additional fields.

-How- can I do this with Django 1.8+?

Thanks for any help,

Community
  • 1
  • 1
jeff
  • 13,055
  • 29
  • 78
  • 136

3 Answers3

1

An article about the topic: How to Extend Django User Model

What I use is the One-To-One approach:

class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name="profile")
    #more fields

    @staticmethod
    @receiver(post_save, sender=User, dispatch_uid="at_user_post_save")
    def user_post_save(sender, instance, **kwargs):
        #create new profile on user.save() (if necessary)
        profile, new = UserProfile.objects.get_or_create(user=instance)


#in view
profile = request.user.profile

UPDATE:

Are there any other caveats? Like what happens when I remove a UserProfile?

UserProfile is the one who holds the relation, so on delete no user should be deleted. You can control what must be the behavior when a user gets deleted via the on_delete kwarg.

Also do they always have the same private keys?

No each class have its own PKs, just the UserProfile holds the PK to its user.

OneToOneField is in conceptually a ForeignKey with an unique=True, the big difference is that the reverse side of the relation do not return a list with 0/1 elements, but the element itself or raise DoesNotExist or None if null=True.

The only thing which I don't like in this approach is that you always have to do 1 more query to get user.profile. I still can't find a good and clean approach to always .select_related('profile') when a new user is fetched from auth, but this is more of a problem of the authentication system rather than the approach itself.

Todor
  • 15,307
  • 5
  • 55
  • 62
  • This method seems more suitable now :) Any objections? – jeff Oct 20 '16 at 13:56
  • I'm going with this approach. It creates the `UserProfile` as a `User` is created. I just need to get the user like `request.user.profile` in the views, but are there any other caveats? Like what happens when I remove a `UserProfile`? Is the corresponding `User` deleted too? Also do they always have the same private keys? – jeff Oct 20 '16 at 17:45
  • @halilpazarlama updated the question with some answers to your questions. I also posted a link to an article explaining the different approaches with their pros and cons. – Todor Oct 21 '16 at 06:18
1

django-registration supports custom user models, so it won't be a problem to create your own user model and use it with django-registration.

All you need to do is to create registration form based on registration.forms.RegistrationForm and then use it in RegistrationView. You also need to keep email and is_active fields from default django user model and provide email_user method in user model. USERNAME_FIELD also must be specified. Other requirements depends of your authentication method.

More on that you can find in django-registration docs.

GwynBleidD
  • 20,081
  • 5
  • 46
  • 77
1

First req for extending the user model: you have to start with a clean django project that you have not called the: "python manage.py migrate" command on.

This is because if you did migrated in the past, the un-extanded user model is already created an django doesn't know how to change it.

Now, to choose another user model the first thing you have to do is on your settings.py:

AUTH_USER_MODEL = 'APPNAME.Account'

It is recommended to create a new app to handle the user model. be aware not to call the app "account", as it collides with the already existing user model.

I created An app called accountb. on the models:

from django.db import models
from django.contrib.auth.models import UserManager
from django.contrib.auth.models import AbstractUser

class AccountManager(UserManager):
    def create_user(self, email, password=None, **kwargs):
        if not email:
            raise ValueError('Users must have a valid email address.')
        if not kwargs.get('username'):
            raise ValueError('Users must have a valid username.')

        account = self.model(
            email=self.normalize_email(email), 
            username=kwargs.get('username'), 
            year_of_birth = kwargs.get('year_of_birth'),
            #MODEL = kwargs.get('MODEL_NAME'),
        )
        account.set_password(password)
        account.save()

        return account

    def create_superuser(self, email, password, **kwargs):
        account = self.create_user(email, password, **kwargs)
        account.is_staff = True
        account.is_superuser = True
        account.save()

        return account

class Account(AbstractUser):
    email = models.EmailField(unique=True)
    #ADD YOUR MODELS HERE

    objects = AccountManager()

    def __str__(self):
        return self.email

Also, dont forget to register it on admin.py:

from django.contrib import admin
from .models import Account

admin.site.register(Account)
idik
  • 872
  • 2
  • 10
  • 19
  • Thank you so much! This seem to work fine, but I'm just confused about the last part (registering it on `admin.py`) if I do it the way you proposed, I don't see the new fields. When I register it simply by `admin.site.register(Account)`, now I see the new fields. I wonder why is this happening. I will accept your answer anyway but I would appreciate collaboration about this too. Thanks again to all the answerers! – jeff Oct 20 '16 at 12:41
  • Now the registration module is giving me an error. If you are interested, here is my follow up [question](http://stackoverflow.com/questions/40155777/custom-user-models-in-django-no-such-table-auth-user) :) – jeff Oct 20 '16 at 13:36
  • 1
    My bad, that last part was some unrelated code. I edited my answer. – idik Oct 20 '16 at 14:05