Agustin Castro Agustin Castro - 1 month ago 5
Python Question

Can't route reset email link to password reset view

I'm developing a password reset via email in django. I have already sent the email containing the password reset link correctly, the problem is that when the user clicks the password reset link in the email, is being redirected to the "send reset link to mail" instead of the "input new password link".

Url's are as following:

url(r'^login/$', views.Login, name='login'),
url(r'^register/$', views.Register.as_view(), name='register'),
url(r'^reset_password', views.ResetPasswordRequestView.as_view(), name="password_reset"),
url(r'^reset_password_confirm/(?P<uidb64>[0-9A-Za-z]+)-(?P<token>.+)/$', views.PasswordResetConfirmView.as_view(), name='reset_password_confirm'),


The template that the user uses to input his email is this one (is used inside a base templated and striped for the sake of brevity):

<form action="" method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit"/>
</form>


The views that handle the send password reset link:

class ResetPasswordRequestView(FormView):
template_name = 'authentication/password_reset.html'
success_url = 'login'
form_class = PasswordResetRequestForm

def get(self, request, *args, **kwargs):
form = PasswordResetRequestForm()
return render(request, 'authentication/password_reset.html', {'form': form})

def post(self, request, *args, **kwargs):
'''
A normal post request which takes input from field "email" (in ResetPasswordRequestForm).
'''
form = self.form_class(request.POST)
form_email = ''
if form.is_valid():
form_email = form.cleaned_data["email"]
if self.validate_email_address(form_email): # uses the method written above
'''
Verifies that the submited email is valid and gets its asociated user. If it is registered an
email will be sent to that address with the reset link
'''
user = User.objects.get(email=form_email)
if user is not None:
email_template_data = {
'email': user.email,
'domain': request.META['HTTP_HOST'],
'site_name': 'Collector',
'uid': urlsafe_base64_encode(force_bytes(user.pk)),
'user': user,
'token': default_token_generator.make_token(user),
'protocol': 'http',
}
email_template_name = 'authentication/password_reset_email.html'
subject = 'Password reset on Collector'
email = loader.render_to_string(email_template_name, email_template_data)

send_mail(subject, email, DEFAULT_FROM_EMAIL, [user.email], fail_silently=False)
result = self.form_valid(form)
messages.success(request,
'An email has been sent to ' + form_email + ". Please check its inbox to continue reseting password.")
return result
else:
result = self.form_invalid(form)
messages.error(request, 'No user is associated with this email address')
return result

messages.error(request, 'Invalid Input')
return self.form_invalid(form)


The view that handles the input of the nre password is as following:

class PasswordResetConfirmView(FormView):
"""
View that checks the hash in a password reset link and presents a
form for entering a new password.
"""
template_name = "authentication/password_reset_enter_new.html"
success_url = 'login'
form_class = SetPasswordForm

def get(self, request, uidb64=None, token=None, *arg, **kwargs):
form = SetPasswordForm()
return render(request, "authentication/password_reset_enter_new.html", {'form': form})

def post(self, request, uidb64=None, token=None, *arg, **kwargs):

UserModel = User
form = self.form_class(request.POST)
assert uidb64 is not None and token is not None # checked by URLconf
try:
uid = urlsafe_base64_decode(uidb64)
user = UserModel._default_manager.get(pk=uid)
except (TypeError, ValueError, OverflowError, UserModel.DoesNotExist):
user = None

if user is not None and default_token_generator.check_token(user, token):
if form.is_valid():
new_password = form.cleaned_data['new_password']
user.set_password(new_password)
user.save()
messages.success(request, 'Password has been reset.')
return self.form_valid(form)
else:
messages.error(request, 'Password reset has not been unsuccessful.')
return self.form_invalid(form)
else:
messages.error(request, 'The reset password link is no longer valid.')
return self.form_invalid(form)


My problem is that the url being sent to the user doesnt route to the PasswordResetConfirmView, this url is the following:

127.0.0.1:8000/authentication/reset_password_confirm/MQ-4gk-262cd9422172a2e21be1/


Any help or input is highly appreciated!

Answer

You need to terminate your reset_password URL:

url(r'^reset_password/$', ...)

otherwise it will match everything beginning with that string.

Also note that Django has most of this functionality built in already.

Comments