Introduction
I wanted to add username
field to password reset page in Django.
Since Django v2, auth view has migrated to Class View and I weren’t able to find clear instruction online on how to achieve this.
So this page is for you guys having the same intention.
Description of Class View can be found here.
Django Form
Django by default uses PasswordResetForm and it only includes email field in its form.
Full code of PasswordResetForm can be found here.
Inherit this class as below and add the username field.
from django import forms
from django.contrib.auth.forms import PasswordResetForm
class MyPasswordResetForm(PasswordResetForm):
username = forms.CharField(max_length=254)
field_order = ['username', 'email']
Very clean :)
When you add extra field like this, it will appear after the fields from PasswordResetForm, ie, after the email field.
Since I want email to come after username, I’ve declared field_order.
Django View
We now inherit the default view class, PasswordResetView, to handle our custom Form.
Full code of PasswordResetView can be found here.
from django.shortcuts import render, redirect
from django.contrib.auth import get_user_model
from django.contrib.auth.views import PasswordResetView
from .forms import MyPasswordResetForm
class MyPasswordResetView(PasswordResetView):
form_class = MyPasswordResetForm
def form_valid(self, form):
username = form.cleaned_data.get('username')
email = form.cleaned_data.get('email', '').lower()
try:
user = get_user_model().objects.get(username=username, email=email)
except(get_user_model().DoesNotExist):
user = None
if user is None:
return redirect('password_reset_done')
return super().form_valid(form)
Override the class attribute form_class
and tell to use our own View.
Override the form_valid
method and add codes to handle username.
Above code will search for user using the username and email, assuming either or both field is unique so multi result does not return.
If user found, continue with the reset process and send email.
If user not found, don’t send email and redirect to ‘password_reset_done’ page (user not found is not reported to user for security).
Django HTML template
Create template for the view. No extra modification here.
I am using default path: templates\registration\password_reset_form.html
<form method="post">
{% csrf_token %}
{% for field in form %}
<div>
{{ field.label_tag }}
{{ field }}
{% if field.help_text %}<p>{{ field.help_text }}</p>{% endif %}
{% for error in field.errors %}<p>* {{ error|escape }}</p>{% endfor %}
</div>
{% endfor %}
<button type="submit">Submit</button>
</form>
Django URL
Last of all, include the view MyPasswordResetView to your urls file.
from django.urls import include, path
from . import views
urlpatterns = [
path('password_reset/', views.MyPasswordResetView.as_view(), name='password_reset'),
path('', include('django.contrib.auth.urls')), # first match, first served
]
Result
That’s it! My password reset page now looks like this: