2

I want to reset password in django app by visiting a certain url and passing e-mail by POST method. That's because I want to use it in an android app, which will have its own form. So, I thought I could create a custom password reset form and then save it, but it doesn work.

from django.contrib.auth.forms import PasswordResetForm

class Custom_password_reset_form(PasswordResetForm):
    def __init__(self, email, **kwargs):
        super(Custom_password_reset_form, self).__init__(**kwargs)
        self.email = forms.EmailField(initial=email, max_length=254)


def mobile_password_reset(request):
    """Sends mail with password reset link."""
    email = request.POST.get('email')
    my_form = Custom_password_reset_form(email)

    if my_form.is_valid():
        print 'valid'
        my_form.save()
    else:
        print 'not valid'
        print my_form._errors
    return render(...)

Validation always fails, giving empty my_form._errors list. What am I missing here? Or maybe there is some better way to do this?

Correct code:

The custom class is unnecessary, and should be removed. The password resetting method should look like this:

def mobile_password_reset(request):
    """Sends mail with password reset link."""
    my_form = PasswordResetForm(request.POST)
    if my_form.is_valid():
        my_form.save()
        return JsonResponse({'success': True})
    else:
        return JsonResponse({'success': False})

It's important to note, that without django.contrib.sites app added to settings, my_form.save() will not work. It could be also fixed by adding this argument: my_form.save(request=request).

2 Answers 2

2

A form needs to be bound to data to be valid. You are passing the email argument but no data, so the form will always be unbound. For unbound forms, is_valid() returns False, but there are no errors.

I don't understand what your custom form is required for. If the email is in request.POST, why not just pass request.POST to the regular PasswordRestForm?

my_form = PasswordResetForm(request.POST)
Sign up to request clarification or add additional context in comments.

Comments

0

You can do this by overriding django's auth module. Add following urls in your urls.py

url(r'^password_reset/$', PasswordReset.as_view(is_admin_site=True),
    {'is_admin_site': 'True'}),

After this, add following views

class PasswordReset(RedirectView):
is_admin_site = False
template_name = 'forget_password.html'
email_template_name = 'password_reset_email.html'
subject_template_name = 'password_reset_subject.html'
token_generator = default_token_generator
post_reset_redirect = None
from_email = None,
current_app = None,
extra_context = None

def get(self, request, *args, **kwargs):

    form = YourCustomPasswordResetForm()
    context = {
        'form': form,
    }
    if self.extra_context is not None:
        context.update(self.extra_context)
    return TemplateResponse(request, self.template_name, context,
                            current_app=self.current_app)

def post(self, request, *args, **kwargs):
    form = YourCustomPasswordResetForm(request.POST)
    if form.is_valid():
        if self.from_email is not None:
            from_email = 'From Email'
        opts = {
            'use_https': request.is_secure(),
            'token_generator': self.token_generator,
            'from_email': self.from_email,
            'email_template_name': self.email_template_name,
            'subject_template_name': self.subject_template_name,
            'request': request,
        }
        if self.is_admin_site:
            opts = dict(opts, domain_override=request.get_host())
        form.save(**opts)
        return HttpResponseRedirect(self.post_reset_redirect)
    context = {
        'form': form,
    }
    if self.extra_context is not None:
        context.update(self.extra_context)
    return TemplateResponse(request, self.template_name, context,
                            current_app=self.current_app)

In you forms, add you form and add folloing save method in it.

def save(self, domain_override=None,
         subject_template_name='registration/password_reset_subject.txt',
         email_template_name='registration/password_reset_email.html',
         use_https=False, token_generator=default_token_generator,
         from_email=None, request=None):
    """
    Generates a one-use only link for resetting password and sends to the
    user.
    """
    from_email = 'From Email'
    from django.core.mail import send_mail

    user = User.objects.get(username=request.POST['username'])
    if not domain_override:
        current_site = get_current_site(request)
        site_name = current_site.name
        domain = current_site.domain
    else:
        site_name = domain = domain_override
        c = {
            'email': user.email,
            'domain': domain,
            'site_name': site_name,
            'uid': int_to_base36(user.id),
            'user': user,
            'token': token_generator.make_token(user),
            'protocol': use_https and 'https' or 'http',
        }
    subject = loader.render_to_string(subject_template_name, c)
    # Email subject *must not* contain newlines
    subject = ''.join(subject.splitlines())
    email = loader.render_to_string(email_template_name, c)
    send_mail(subject, email, from_email, [user.email])

When you will submit that form, user will get email with a link containing a token. That link can only used once.

1 Comment

I haven't tested your answer, but it seems to be too heavy for my issue.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.