How to customize user profile when using django-allauth

I have a django project with the django-allauth app. I need to collect additional data from the user at signup. I came across a similar question here but unfortunately, no one answered the profile customization part.

Per the documentation provided for django-allauth:

ACCOUNT_SIGNUP_FORM_CLASS (=None)

A string pointing to a custom form class (e.g. ‘myapp.forms.SignupForm’) that is used during signup to ask the user for additional input (e.g. newsletter signup, birth date). This class should implement a ‘save’ method, accepting the newly signed up user as its only parameter.

I am new to django and am struggling with this. Can someone provide an example of such a custom form class? Do I need to add a model class as well with a link to the user object like this ?


Solution 1:

Suppose you want to ask the user for his first/last name during signup. You'll need to put these fields in your own form, like so:

class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30, label='Voornaam')
    last_name = forms.CharField(max_length=30, label='Achternaam')

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()

Then, in your settings point to this form:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourproject.yourapp.forms.SignupForm'

Note that SignupForm cannot be defined in the same file as form overrides through ACCOUNT_FORMS or SOCIALACCOUNT_FORMS, because that would lead to a circular import error.

That's all.

Solution 2:

Using the solution suggested by pennersr I was getting a DeprecationWarning:

DeprecationWarning: The custom signup form must offer a def signup(self, request, user) method DeprecationWarning)

This is because as of version 0.15 the save method has been deprecated in favour of a def signup(request, user) method.

So to solve this, the code of the example should be like this:

class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30, label='Voornaam')
    last_name = forms.CharField(max_length=30, label='Achternaam')

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()

Solution 3:

Here's what worked for me combining a few of the other answers (none of them are 100% complete and DRY).

In yourapp/forms.py:

from django.contrib.auth import get_user_model
from django import forms

class SignupForm(forms.ModelForm):
    class Meta:
        model = get_user_model()
        fields = ['first_name', 'last_name']

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        user.save()

And in settings.py:

ACCOUNT_SIGNUP_FORM_CLASS = 'yourapp.forms.SignupForm'

This way it uses the model forms so that it's DRY, and uses the new def signup. I tried putting 'myproject.myapp.forms.SignupForm' but that resulted in a error somehow.

Solution 4:

@Shreyas: The below solution may not be the cleanest, but it works. Please let me know if you have any suggestions to clean it up any further.

To add information that does not belong to the default user profile, first create a model in yourapp/models.py. Read the general django docs to learn more about it, but basicly:

from django.db import models

class UserProfile(models.Model):
    user = models.OneToOneField(User, related_name='profile')
    organisation = models.CharField(organisation, max_length=100, blank=True)

Then create a form in yourapp/forms.py:

from django import forms

class SignupForm(forms.Form):
    first_name = forms.CharField(max_length=30, label='Voornaam')
    last_name = forms.CharField(max_length=30, label='Achternaam')
    organisation = forms.CharField(max_length=20, label='organisation')

    def signup(self, request, user):
        user.first_name = self.cleaned_data['first_name']
        user.last_name = self.cleaned_data['last_name']
        # Replace 'profile' below with the related_name on the OneToOneField linking back to the User model
        up = user.profile
        up.organisation = self.cleaned_data['organisation']
        user.save()
        up.save()