Extending User profile in Django 1.7

django, django-models

Solution

I have looked at all the answers but none does really hold the solution for my problem (though some of you gave me quite good hints for looking in the right direction). I will summarize here the solution I have found to solve my problem.

First of all, I have to admit I didn't tell everything about my problem. I wanted to insert extra fields in the `User` model and use other apps such as the default authentication scheme of Django. So, extending the default `User` by inheritance and setting `AUTH_USER_MODEL` was a problem because the other Django applications were stopping to work properly (I believe they didn't use `user = models.OneToOneField(settings.AUTH_USER_MODEL)` but `user = models.OneToOneField(User)`).

As, it would have been too long to rewrite properly the other applications I am using, I decided to add this extra field through a One-to-One field. But, the documentation miss several points that I would like to fill in the following.

So, here is a complete example of adding an extra field to the `User` model with other applications using the same model.

First, write the description of the model gathering the extra fields that you want to add to your `models.py` file:

from django.contrib.auth.models import User

class UserProfile(models.Model):
  user = models.OneToOneField(User)

  extra_field = models.CharField(max_length=100)

Then, we need to trigger the addition of an object `UserProfile` each time a `User` is created. This is done through attaching this code to the proper signal in the `receiver.py` file:

from django.contrib.auth.models import User
from django.db.models.signals import post_save
from django.dispatch import receiver

from my_user_profile_app.models import UserProfile

@receiver(post_save, sender=User)
def handle_user_save(sender, instance, created, **kwargs):
  if created:
    UserProfile.objects.create(user=instance)

Now, if you want to be able to modify it through the administration interface, just stack it with the usual `UserAdmin` form in the `admin.py` file.

from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from django.contrib.auth.models import User

from my_user_profile_app.models import UserProfile

# Define an inline admin descriptor for UserProfile model
class UserProfileInline(admin.StackedInline):
  model = UserProfile
  can_delete = False

# Define a new User admin
class UserAdmin(UserAdmin):
  inlines = (UserProfileInline, )

# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, UserAdmin)

Then, it is time now to try to mix this extra field with the default Django authentication application. For this, we need to add an extra field to fill in the `SignupForm` and the `SettingsForm` through inheritance in the `forms.py` file:

import account.forms
from django import forms

class SignupForm(account.forms.SignupForm):
  extra_field = forms.CharField(label="Extra Field", max_length=100)

class SettingsForm(account.forms.SignupForm):
  extra_field = forms.CharField(label="Extra Field", max_length=100)

And, we also need to add some code to display and get properly the data that you have been added to the original `User` model. This is done through inheritance onto the `SignupView` and the `SettingsView` views in the `views.py` file:

import account.views

from my_user_profile_app.forms import Settings, SignupForm
from my_user_profile_app.models import UserProfile


class SettingsView(account.views.SettingsView):
  form_class = SettingsForm

  def get_initial(self):
    initial = super(SettingsView, self).get_initial()

    initial["extra_field"] = self.request.user.extra_field

    return initial

  def update_settings(self, form):
    super(SettingsView, self).update_settings(form)

    profile = self.request.user.userprofile
    profile.extra_field = form_cleaned_data['extra_field']
    profile.save()


class SignupView(account.views.SignupView):
  form_class = SignupForm

  def after_signup(self, form):
    profile = self.created_user.userprofile
    profile.extra_field = form_cleaned_data['extra_field']
    profile.save()

    super(SignupView, self).after_signup(form)

Once everything is in place, it should work nicely (hopefully).

Problem

I know, this question has been already asked many times in SO, but most of the answers I read were either outdated (advising the now deprecated `AUTH__PROFILE_MODULE` method), or were lacking of a concrete example. So, I read the Django documentation [1,2], but I lack a real example on how to use it properly. In fact, my problem comes when a new user is created (or updated) through a form. The user is obviously created but, the fields from the extension are all unset. I know that the Django documentation is stating that: These profile models are not special in any way - they are just Django models that happen to have a one-to-one link with a User model. As such, they do not get auto created when a user is created, but a `django.db.models.signals.post_save` could be used to create or update related models as appropriate. But, I don't know how to do it in practice (should I add a a `receiver` and if 'yes', which one). For now, I have the following (taken from the documentation for the sake of brevity): File `models.py` ``` from django.contrib.auth.models import User class Employee(models.Model): user = models.OneToOneField(User) department = models.CharField(max_length=100) ``` File `admin.py` ``` from django.contrib import admin from django.contrib.auth.admin import UserAdmin from django.contrib.auth.models import User from my_user_profile_app.models import Employee # Define an inline admin descriptor for Employee model class EmployeeInline(admin.StackedInline): model = Employee can_delete = False verbose_name_plural = 'employee' # Define a new User admin class UserAdmin(UserAdmin): inlines = (EmployeeInline, ) # Re-register UserAdmin admin.site.unregister(User) admin.site.register(User, UserAdmin) ``` File `forms.py` ``` class SignupForm(account.forms.SignupForm): department = forms.CharField(label="Department", max_length=100) class SettingsForm(account.forms.SignupForm): department = forms.CharField(label="Department", max_length=100) ``` Then, in my code, I use it like this: ``` u = User.objects.get(username='fsmith') freds_department = u.employee.department ``` But, Signup and Settings forms do not operates as expected and new values for the `departement` is not recorded. Any hint is welcome !

Original source