Customizing Django Allauth Signup Forms
Tags: django, python, webdev
Published: 2020-09-14 18:31:52

Sometimes you want to modify Django signup forms to include more data fields. In this tutorial, I have a fictional app called "SpyBook", a social network for secret operatives. At launch there are only two types of users, "Spy" and "Driver". Users need to be able to choose which one they are.

Our example will build off code from my [Cookiecutter Django](https://github.com/pydanny/cookiecutter-django) project, but should be portable to any project using Django Allauth.

# Step 1: Tell Django-Allauth to Use A Customized Signup Form

At the bottom of `config/settings/base.py`, past in the following code snippet:

```python
# Control the forms that django-allauth uses
ACCOUNT_FORMS = {
    "login": "allauth.account.forms.LoginForm",
    "add_email": "allauth.account.forms.AddEmailForm",
    "change_password": "allauth.account.forms.ChangePasswordForm",
    "set_password": "allauth.account.forms.SetPasswordForm",
    "reset_password": "allauth.account.forms.ResetPasswordForm",
    "reset_password_from_key": "allauth.account.forms.ResetPasswordKeyForm",
    "disconnect": "allauth.socialaccount.forms.DisconnectForm",
    # Use our custom signup form
    "signup": "spybook.users.forms.SpyBookSignupForm",
}
```

# Step 2: Import Django Forms

In `<yourprojectname>/users/forms.py`, Cookiecutter Django imports `django.contrib.auth.forms` as `forms`. This is a namespace problem since typically `django.forms` is normally imported as plain `forms`. To address that, let's just import `django.forms` under a different namespace:

``` python
# Importing django.forms under a special namespace because of my old mistake
from django import forms as d_forms
```

# Step 3: Import django-allauth's SignUp Form

Right under that forms import, get the `SignUpForm`:

``` python
from allauth.account.forms import SignupForm
```

# Step 4: Write Our Custom SignUp Form

Rather than write out a long explanation, I'll just comment every line of code. 

``` python
# SpyBookSignupForm inherits from django-allauth's SignupForm
class SpyBookSignupForm(SignupForm):

    # Specify a choice field that matches the choice field on our user model
    type = d_forms.ChoiceField(choices=[("SPY", "Spy"), ("DRIVER", "Driver")])

    # Override the init method
    def __init__(self, *args, **kwargs):
        # Call the init of the parent class
        super().__init__(*args, **kwargs)
        # Remove autofocus because it is in the wrong place
        del self.fields["username"].widget.attrs["autofocus"]

    # Put in custom signup logic
    def custom_signup(self, request, user):
        # Set the user's type from the form reponse
        user.type = self.cleaned_data["type"]
        # Save the user's type to their database record
        user.save()
```

I wrote this out on my live stream, which you can watch on my [youtube](https://youtube.com/c/danielfeldroy) and [twitch](https://twitch.tv/danielfeldroy) channels. I'll also be putting together a short video presentation of how this works. Of course, the code for this can be found on [GitHub](https://github.com/pydanny/multiple-user-types-django).

If you want to learn all kinds of advanced tricks and tips, take a look at my ice-cream themed [book](https://www.feldroy.com/products/two-scoops-of-django-3-x) and [course](https://www.feldroy.com/products/two-scoops-of-django-3-x?variant=32311272013911) on Django Best Practices.