Why am I getting this key error when using django-formtools and django-allauth together
So I am trying to create multi-step signup wizard using django-formtools and django-allauth. I am using the SessionWizardView from formtools and just trying to use allauth's functionality for signup. I'm not using its standard one page signup flow or its form classes or default signup template.
I believe I have most things configured and setup correctly, however, I keep getting this error from django when I go to /accounts/signup/
KeyError at /accounts/signup/
'email_step'
Exception Value:'email_step'
it happens on line 50 in the views.py file, which is this line:
return [TEMPLATES[self.steps.current]]
I will leave out the imports for brevity sake, but below you will find all the relevant code, should you need more, just ask.
in my settings.py:
# allauth settings for dev for now
ACCOUNT_EMAIL_VERIFICATION = 'optional'
ACCOUNT_SIGNUP_FIELDS = ['email*', 'password1*', 'password2*']
ACCOUNT_LOGIN_METHODS = {'email'}ACCOUNT_UNIQUE_EMAIL = TrueACCOUNT_LOGIN_METHODS = {'email'}
ACCOUNT_USER_MODEL_USERNAME_FIELD = NoneACCOUNT_LOGOUT_REDIRECT_URL = '/'
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
LOGIN_REDIRECT_URL = '/'
ACCOUNT_ADAPTER = 'users.adapters.CustomAccountAdapter'
# telling allauth what forms to use
ACCOUNT_FORMS = { 'signup': 'users.forms.EmailForm', ...}
in my forms.py:
class EmailForm(forms.Form):
email = forms.EmailField(max_length=255, required=True, label='Email')
class PasswordForm(forms.Form):
password = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Confirm Password', widget=forms.PasswordInput)
def clean(self):
cleaned_data = super().clean()
password = cleaned_data.get('password')
password2 = cleaned_data.get('password2')
if password and password2 and password != password2:
raise forms.ValidationError('Passwords do not match.')
return cleaned_data
in my views.py:
class SignUpWizard(SessionWizardView):
def get_template_names(self):
return [TEMPLATES[self.steps.current]] # this line causes the error
def done(self, form_list, **kwargs):
form_data = process_form_list(form_list)
email = form_data['email_step']['email']
password = form_data['password_step']['password']
adapter = get_adapter(self.request)
user = adapter.new_user(self.request)
user.email = email
adapter.save_user(self.request, user, form=SignupForm(data={'email': email}))
return complete_signup(self.request, user, ACCOUNT_EMAIL_VERIFICATION, LOGIN_REDIRECT_URL)
def process_form_list(form_list):
form_data = {}
for form in form_list:
form_data.update(form.cleaned_data)
return {
'email_step': {'email': form_data['email']},
'password_step': {'password': form_data['password'], 'password2': form_data['password2']},
}
in my projects's urls.py:
path('accounts/signup/', SignUpWizard.as_view(FORMS), name='account_signup'),
You’re getting KeyError: 'email_step' because self.steps.current returns the step name, and your TEMPLATES dict doesn’t have a matching key.
If your wizard is defined like:
FORMS = [
('email_step', EmailForm),
('password_step', PasswordForm),
]
Then:
self.steps.current # 'email_step'
So this:
return [TEMPLATES[self.steps.current]]
requires:
TEMPLATES = {
'email_step': 'users/signup_email.html',
'password_step': 'users/signup_password.html',
}
If your keys are '0', '1', etc., you’ll get the KeyError.
Fix: Make your TEMPLATES keys match your wizard step names exactly.