NoReverseMatch at /reset-password-confirm/

28 views Asked by At

Im trying to implement Reset Password View by using View class. Actually I faced with an error that is annoying.

I have 3 Views for implementing:

  1. UserResetPasswordView Here User enters email and a request sends to his/her email
  2. UserResetPasswordRequestSentView Here shows a message that email has sent
  3. UserResetPasswordConfirm after 2 views above, this view let's the user change password.

after redirecting user to UserResetPasswordRequestSentView, an email will be send to the user's email to access the user to change his/her password.

so user will be redirect to UserResetPasswordConfirm that contains inputs for setting new password.

This is my urls.py script:

urlpatterns = [
    path(
        route='reset-password',
        view=views.UserResetPasswordView.as_view(),
        name='reset',
    ),
    
    path(
        route='reset-password-request-sent',
        view=views.UserResetPasswordRequestSentView.as_view(),
        name='reset_sent',
    ),
    
    path(
        route='reset-password-confirm/<uidb64>/<token>',
        view=views.UserResetPasswordConfirm.as_view(),
        name='reset_confirm',
    ),
]

this is UserResetPasswordView view:

class UserResetPasswordView(View):
    def get(self, request: HttpRequest) -> HttpResponse:
        reset_password_form: ResetPasswordForm = ResetPasswordForm()
        return render(
            request=request,
            template_name='reset/reset.html',
            context={
                'reset_password_form': reset_password_form,
            },
        )
        
    def post(self, request: HttpRequest) -> Union[HttpResponseRedirect, HttpResponsePermanentRedirect, HttpResponse]:
        reset_password_form: ResetPasswordForm = ResetPasswordForm(request.POST or None)
        if (reset_password_form.is_valid()):
            cd = reset_password_form.cleaned_data
            user = get_user_model().objects.filter(Q(email=cd['email'])).first()
            if (user):
                subject = 'درخواست بازنشانی رمز ورود'
                message = render_to_string(
                    template_name='email/template_reset_password.html',
                    context={
                        'user': user,
                        'domain': get_current_site(request=request).domain,
                        'uid': urlsafe_base64_encode(force_bytes(user.pk)),
                        'token': account_activation_token.make_token(user),
                        'protocol': 'https' if request.is_secure() else 'http',
                    }
                )
                send_mail(
                    subject,
                    message,
                    'settings.EMAIL_HOST_USER',
                    [cd['email']],
                    fail_silently=False
                )
                return redirect(to=reverse('reset_sent'))
            else:
                return redirect(to=reverse('reset'))
        
        return render(
            request=request,
            template_name='reset/reset.html',
            context={
                'reset_password_form': reset_password_form,
            },
        )

this is the form that user can set new password(UserResetPasswordConfirm):

class UserResetPasswordConfirm(View):
    def get(self, request: HttpRequest, uidb64, token) -> HttpResponse:
        try:
            uid = force_str(urlsafe_base64_decode(uidb64))
            user_obj = User.objects.get(pk=uid)
        except:
            user_obj = None
        if (user_obj is not None and account_activation_token.check_token(user_obj, token)):
            form: ResetPasswordConfirmForm = ResetPasswordConfirmForm()
        else:
            messages.error(
                request=request,
                message='توکن شما منقضی شده است',
            )
        return render(
            request=request,
            template_name='reset/reset-confirm.html',
            context={
                'form': form,
            },
        )
    
    def post(self, request: HttpRequest, uidb64, token) -> Union[HttpResponseRedirect, HttpResponsePermanentRedirect, HttpResponse]:
        try:
            uid = force_str(urlsafe_base64_decode(uidb64))
            user_obj = User.objects.get(pk=uid)
        except:
            user_obj = None
        if (user_obj is not None and account_activation_token.check_token(user_obj, token)):
            form: ResetPasswordConfirmForm = ResetPasswordConfirmForm(request.POST or None)
            if (form.is_valid()):
                cd = form.cleaned_data
                if (cd['new_password'] != cd['con_new_password']):
                    form.add_error(
                        field='new_password',
                        error='رمز ورود شما با یکدیگر مطابقت ندارد',
                    )
                else:
                    user_obj.set_password(raw_password=cd['new_password'])
                    user_obj.save()
                    return redirect(to=reverse('reset_complete'))
            else:
                form.add_error(
                    field='new_password',
                    error='رمز ورود شما با یکدیگر مطابقت ندارد',
                )
            return render(
                request=request,
                template_name='reset/reset-confirm.html',
                context={
                    'form': form,
                },
            )
        else:
            messages.error(
                request=request,
                message='توکن شما منقضی شده است',
            )
        return render(
            request=request,
            template_name='reset/reset-confirm.html',
            context={
                'form': form,
            },
        )

this is the form that contains inputs to set new password for user:

{% extends "base.html" %}
{% load static %}
{% block title %}
    <title>
        تنظیم رمز ورود جدید
    </title>
{% endblock %}
{% block body %}
    <div class="container text-center mt-5">
        <form class="form-inline" action="{% url 'reset_confirm' %}" method="post">
            {% csrf_token %}
            <div class="row">
                <h6 class="text-info mt-2">رمز ورود جدید حداقل بین 8 تا 64 کاراکتر (ترکیب حروف و اعداد)</h6>
                <div class="field">
                    <p class="control has-icons-left">
                        {{ form.new_password }}
                        <span class="icon is-small is-left">
                            <i style="color: #1F2330;" class="fas fa-lock"></i>
                        </span>
                    </p>
                </div>
                <h6 class="text-info mt-2">تایید مجدد رمز ورود جدید</h6>
                <div class="field">
                    <p class="control has-icons-left">
                        {{ form.con_new_password }}
                        <span class="icon is-small is-left">
                            <i style="color: #1F2330;" class="fas fa-lock"></i>
                        </span>
                    </p>
                </div>
            </div>
            <div class="row">
                <div class="col">
                    <button style="width: 150px;" id="set_pass" type="submit" class="btn btn-info text-dark">ارسال درخواست</button>
                </div>
            </div>
        </form>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.1/jquery.min.js" integrity="sha512-aVKKRRi/Q/YV+4mjoKBsE4x3H+BkegoM/em46NNlCqNTmUYADjBbeNefNxYV7giUp0VxICtqdrbqU7iVaeZNXA==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <script>
        jQuery(function($){
            $(document).ajaxSend(function() {
                $('spinner-border').fadeIn(580);

                var loading_tag = '<div class="spinner-border"></div>&nbsp;&nbspلطفا صبر کنید';
                $('#set_pass').html(loading_tag);
            });

            $('#set_pass').click(function() {
                $.ajax({
                    type: 'GET',
                    success: function(data) {
                        console.log(data);
                    }
                }).done(function() {
                    setTimeout(function() {
                        $('.spinner-border').fadeOut(500);
                    }, 700);
                });
            });
        });
    </script>
{% endblock %}

here the user should open the link that sent by email but everytime I try to redirect user to UserResetPasswordConfirm, django shows me this error:

enter image description here

I would be really thankful if you help me.

1

There are 1 answers

0
willeM_ Van Onsem On

Render with the uidb64 and token, so:

class UserResetPasswordConfirm(View):
    def get(self, request: HttpRequest, uidb64, token) -> HttpResponse:
        try:
            uid = force_str(urlsafe_base64_decode(uidb64))
            user_obj = User.objects.get(pk=uid)
        except:
            user_obj = None
        if user_obj is not None and account_activation_token.check_token(
            user_obj, token
        ):
            form: ResetPasswordConfirmForm = ResetPasswordConfirmForm()
        else:
            messages.error(request=request, message='توکن شما منقضی شده است')
        return render(
            request=request,
            template_name='reset/reset-confirm.html',
            context={'form': form, 'uidb64': uidb64, 'token': token},
        )

    # …

and then in the view use these to determine the URL:

<form class="form-inline" action="{% url 'reset_confirm' uidb64 token %}" method="post">

Note: redirect(…) [Django-doc] normally takes the name of the view and (optionally) kwargs, so you should not use reverse(…) [Django-doc]. You thus might want to rewrite redirect(redirect('reset_complete')) to redirect('reset_complete').


Note: Subclassing the View class directly is often not necessary. In this case your view looks like a FormView [Django-doc]. By using a FormView instead of a simple View, you often do not have to implement a lot of boilerplate code.