0

My app sits on Django 1.9.2 and Django REST Framework 3.3.2, with a single page app on the front-end. I must admit I'm new to Django but the docs are unmatched.

I'm trying to implement a custom PasswordResetForm view. My strategy is as follow:

  1. User uses on a front-end form that POSTs data (email string) to an API endpoint (api/v1/password/reset) when she wants to reset her password.
  2. If email is found in DB, send an email and return a successful response.

For part 1, here's the relevant code:

# urls.py

url(r'^api/v1/password/reset/?$', PasswordResetView.as_view(), name='password-reset-link')

-

# views.py

class PasswordResetView(GenericAPIView):
    serializer_class = PasswordResetSerializer
    permission_classes = (AllowAny,)

    def post(self, request, *args, **kwargs):
        # Use DRF serializer to validate data.
        serializer = self.get_serializer(data=request.data)

        # Ensure data is valid before proceeding.
        if serializer.is_valid() == False:
            return Response('error': serializer.errors, 'message': 'Error while attempting to reset password'}, status.HTTP_400_BAD_REQUEST)

        else:
            # Ensure the user exists.
            existing_user = get_user_model().objects.filter(
                email=serializer.validated_data.get('email')
            )

            if not existing_user:
                return Response({'error': {'code': 'email_not_in_db'}}, status.HTTP_404_NOT_FOUND)

        # Validated data fed to a child class of PasswordResetForm.
        form = UserForgotPasswordForm(serializer.validated_data)

        if form.is_valid():
            path = os.path.join(os.path.dirname(os.path.abspath(__file__ + '../../')), 'templates/registration/password_reset_email.html')

            try:
                # Save form, effectively attempting to trigger mail sending. 
                # Unfortunately an exception gets thrown!
                form.save(from_email='no-reply@abc.xyz', email_template_name=path, request=request)

                return Response({'message': 'Password reset request sent'}, status=status.HTTP_200_OK)

            except Exception as e:
                return Response({'error': str(e), 'message': 'Error while attempting to reset password'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

-

# forms.py

class UserForgotPasswordForm(PasswordResetForm):
    email = forms.EmailField(required=True, max_length=254)

    class Meta:
        model = CustomUser
        fields = ('email',)

-

# my_app/templates/registration/password_reset_email.html

{% load i18n %}{% autoescape off %}
{% blocktrans %}You're receiving this email because you requested a password reset for your user account.{% endblocktrans %}

{% trans "Please go to the following page and choose a new password:" %}
{% block reset_link %}
{{ protocol }}://{{ domain }}{% url 'password-reset-link' uidb64=uid token=token %}
{% endblock %}
{% trans "Your username, in case you've forgotten:" %} {{ user.get_username }}

{% endautoescape %}

-

But I get the following error:

Reverse for 'password-reset-link' with arguments '()' and keyword arguments '{'uidb64': b'MTI1', 'token': '4g9-f370fd6ee48d90a40b67'}' not found. 1 pattern(s) tried: ['api/v1/password/reset/?$']

Any idea why? What am I missing? Thanks for your help.

Davy
  • 87
  • 1
  • 11

2 Answers2

1

In the url in your html you are passing a token and a uidb64:

{{ protocol }}://{{ domain }}{% url 'password-reset-link' uidb64=uid token=token %}

However, in your actual url, you do not reference the token or uidb64:

url(r'^api/v1/password/reset/?$', PasswordResetView.as_view(), name='password-reset-link')

I would recommend changing your url to:

url(r'^api/v1/password/reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$', PasswordResetView.as_view(), name='password-reset-link')
jape
  • 2,861
  • 2
  • 26
  • 58
1

Your URL named password-reset-link doesn't support the keyword arguments uidb64 and token that you're sending it. Those keyword arguments aren't just extra data, they need to match somewhere in the URL pattern.

According to the error, your URL pattern is:

api/v1/password/reset/?$

In order to support what you want you'd need to put two named arguments called <uidb64> and <token>. For example:

api/v1/password/reset/(?P<uidb64>[a-zA-Z0-9]+)/(?P<token>[a-zA-Z0-9]+)/?$

You'll have to adjust your regex to support the uidb64 and token formats.

Soviut
  • 88,194
  • 49
  • 192
  • 260