djangodjango-allauth

Django - Allauth doesnt use the custom signup form


I work with Django-Allauth and React as a FE. I added a parameter called name for for the Custom User I created, I've overridden the SignupForm in Forms.py to save it, and just like the documentation said I changed the settings.py to have

ACCOUNT_FORMS = {'signup': 'XXXX.forms.CustomSignupForm'}

But the form does not get initiated when signing up - i added prints that don't go through. A user is being created - but probably with the default form, and doesn't add the additional parameter I added. I checked and the frontend sends the payload properly.

I have this in forms.py

class CustomUserCreationForm(UserCreationForm):
    name = forms.CharField(max_length=30, required=True, help_text='Enter your name.')

    class Meta:
        model = CustomUser
        fields = ("email", "name")


class CustomSignupForm(SignupForm):
    name = forms.CharField(max_length=40, required=True, help_text='Enter your name.')
    
    def __init__(self, *args, **kwargs):
        print("CustomSignupForm is being initialized")
        super().__init__(*args, **kwargs)
        
    def save(self, request):
        print("im working")
        user = super().save(request)
        user.name = self.cleaned_data['name']
        user.save()
        return user 

urls.py

urlpatterns = [
    path("admin/", admin.site.urls),
    path('', include('XXXX.urls')),
    path('accounts/', include('allauth.urls')),
    path("_allauth/", include("allauth.headless.urls")),
]

I also tried using an ACCOUNT_ADAPTER in settings, and this is how the adapter looks in adapters.py

class CustomAccountAdapter(DefaultAccountAdapter):
#     def get_signup_form_class(self):
#         return CustomSignupForm

But it didn't use the form.

Also tried to edit the CustomUserManager, but the form is a step before it if I'm not mistaken so it didn't help as well. I tried looking everywhere, and in the documentation, but couldn't find a solution or a hint. Thanks a lot!

Edit- adding settings.py

from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent



SECRET_KEY = "XXX"

DEBUG = True

ALLOWED_HOSTS = ['127.0.0.1','localhost']
CORS_ALLOWED_ORIGINS = [
    "http://localhost:3000",
    "http://127.0.0.1:3000"
]
CSRF_TRUSTED_ORIGINS = [
    "http://localhost:3000",
    "http://127.0.0.1:3000"
]
CORS_ALLOW_CREDENTIALS = True

INSTALLED_APPS = [
    "django.contrib.admin",
    "django.contrib.auth",
    "django.contrib.contenttypes",
    "django.contrib.sessions",
    "django.contrib.messages",
    "django.contrib.staticfiles",

    "myapp",
    'rest_framework',
    'corsheaders',

    'allauth',
    'allauth.account',
    'allauth.socialaccount',
    'allauth.socialaccount.providers.facebook',
    'allauth.socialaccount.providers.google',
]

SITE_ID = 1

MIDDLEWARE = [
    'corsheaders.middleware.CorsMiddleware',
    "django.middleware.security.SecurityMiddleware",
    "django.contrib.sessions.middleware.SessionMiddleware",
    "django.middleware.common.CommonMiddleware",
    "django.middleware.csrf.CsrfViewMiddleware",
    "django.contrib.auth.middleware.AuthenticationMiddleware",
    "django.contrib.messages.middleware.MessageMiddleware",
    "django.middleware.clickjacking.XFrameOptionsMiddleware",
    "allauth.account.middleware.AccountMiddleware",
]

ROOT_URLCONF = "project.urls"

TEMPLATES = [
    {
        "BACKEND": "django.template.backends.django.DjangoTemplates",
        "DIRS": [],
        "APP_DIRS": True,
        "OPTIONS": {
            "context_processors": [
                "django.template.context_processors.debug",
                "django.template.context_processors.request",
                "django.contrib.auth.context_processors.auth",
                "django.contrib.messages.context_processors.messages",
                'django.template.context_processors.request',
            ],
        },
    },
]


WSGI_APPLICATION = "project.wsgi.application"



DATABASES = {
    "default": {
        "ENGINE": "django.db.backends.sqlite3",
        "NAME": BASE_DIR / "db.sqlite3",
    }
}



AUTH_USER_MODEL = "myapp.CustomUser"


AUTH_PASSWORD_VALIDATORS = [
    {
        "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
    },
    {"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",},
    {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",},
    {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",},
]

AUTHENTICATION_BACKENDS = [

    # Needed to login by username in Django admin, regardless of `allauth`
    'django.contrib.auth.backends.ModelBackend',

    # `allauth` specific authentication methods, such as login by email
    'allauth.account.auth_backends.AuthenticationBackend',

]

SOCIALACCOUNT_PROVIDERS = {
    'google': {
        'APP': {
            'client_id': '123',
            'secret': '456',
            'key': ''
        }
    }
}


LANGUAGE_CODE = "en-us"

TIME_ZONE = "UTC"

USE_I18N = True

USE_TZ = True



STATIC_URL = "static/"


DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

ACCOUNT_EMAIL_VERIFICATION = "optional"  # or "optional"
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = False
ACCOUNT_LOGIN_BY_CODE_ENABLED = True

ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_FORMS = {'signup': 'app.forms.CustomSignupForm'}


HEADLESS_FRONTEND_URLS = {
   "account_confirm_email": "https://app.project.org/account/verify-email/{key}",

    "account_reset_password": "https://app.project.org/account/password/reset",
    "account_reset_password_from_key": "https://app.project.org/account/password/reset/key/{key}",
    "account_signup": "https://app.project.org/account/signup",

    "socialaccount_login_error": "https://app.project.org/account/provider/callback",
}

Solution

  • An important thing to keep in mind is that there is no one-to-one mapping of headed forms to headless input payloads. For example, while having to enter your password twice makes sense in a headed environment, it is pointless from an API point of view. As a result, the headed forms that can be overridden by means of ACCOUNT_FORMS play no role in the headless environment.

    Instead of overriding the complete signup form via ACCOUNT_FORMS, provide a ACCOUNT_SIGNUP_FORM_CLASS that derives from forms.Form and only lists the additional fields you need. These fields will automatically show up in the headed signup form, and will automatically be validated when posting payloads to the signup endpoint.