I have had a task recently. It was about adding a forms/mechanism for resetting a password in our Django based project. We have had our own registration system ongoing... It's a corporate sector project. So you can not go and register yourself. Admins (probably via LDAP sync) will register your email/login in system. So you have to go there and only set yourself a password. For security reasons you can not register. One word.
First I've tried to find standart decision. From reviewed by me were: django-registration and django password-reset. These are nice tools to install and give it a go. But I've needed a more complex decision. And the idea was that own bicycle is always better. So I've thought of django admin and that it has all the things you need to do this yourself in no time. (Actually it's django.contrib.auth part of django, but used out of the box in Admin UI) You can find views you need for this in there. they are:
Now you will be able to start it running the terminal and executing:
You need to override:
Django templates:
Email template:
I'll write down here one's I've used, according to Django's "Batteries included" philosophy.
registration/password_reset_form.html
registration/password_reset_done.html
registration/password_reset_confirm.html
registration/password_reset_complete.html
registration/password_reset_email.html
SO you're getting password reset system out of the box using only Django included tools. In no time.
Comment me...!
First I've tried to find standart decision. From reviewed by me were: django-registration and django password-reset. These are nice tools to install and give it a go. But I've needed a more complex decision. And the idea was that own bicycle is always better. So I've thought of django admin and that it has all the things you need to do this yourself in no time. (Actually it's django.contrib.auth part of django, but used out of the box in Admin UI) You can find views you need for this in there. they are:
- password_reset
- password_reset_done
- password_reset_confirm
- password_reset_complete
1. Emails config:
Your typical Django project has settings.py variables to be set for enabling SMTP server. Usually you need this on production to send admins emails about 500 errors and/or system warnings. For our needs python has an internal mail server. I prefer to use it for debug purposes. So good method would be to use it only in Debug case. You can do it by adding those lines to your settings.py:if DEBUG: EMAIL_HOST = 'localhost' EMAIL_PORT = 1025 EMAIL_HOST_USER = '' EMAIL_HOST_PASSWORD = '' EMAIL_USE_TLS = False DEFAULT_FROM_EMAIL = 'testing@example.com'Here you will have this custom email server set to alternative port. For production you should enter your Sendmail (or whatever you use there) parameters...
Now you will be able to start it running the terminal and executing:
python -m smtpd -n -c DebuggingServer localhost:1025You can also read abut it here. You will be able to copy/paste email links that should be sent via Django email. Now that you have everything setup for experiments...
2. Config Url's:
urls.pyurlpatterns = patterns('', url(r'^user/password/reset/$', 'django.contrib.auth.views.password_reset', {'post_reset_redirect' : '/user/password/reset/done/'}, name="password_reset"), (r'^user/password/reset/done/$', 'django.contrib.auth.views.password_reset_done'), (r'^user/password/reset/(?P<uidb36>[0-9A-Za-z]+)-(?P<token>.+)/$', 'django.contrib.auth.views.password_reset_confirm', {'post_reset_redirect' : '/user/password/done/'}), (r'^user/password/done/$', 'django.contrib.auth.views.password_reset_complete'), # ... )
3. Add Templates:
You have templates for your reset password forms already. Django admin templates/css/js are used there. But I think you would want custom one's for your needs. SO basically all you may need is overriding those templates, specified down here and place them on your templates Django path.You need to override:
Django templates:
- registration/password_reset_form.html
- registration/password_reset_done.html
- registration/password_reset_confirm.html
- registration/password_reset_complete.html
Email template:
- registration/password_reset_email.html
I'll write down here one's I've used, according to Django's "Batteries included" philosophy.
registration/password_reset_form.html
{% extends "base.html" %} {% block title %}Reset Password{% endblock %} {% block content %} <p>Please specify your email address to receive instructions for resetting it.</p> <form action="" method="post"> <div style="display:none"> <input type="hidden" value="{{ csrf_token }}" name="csrfmiddlewaretoken"> </div> {{ form.email.errors }} <p><label for="id_email">E-mail address:</label> {{ form.email }} <input type="submit" value="Reset password" /></p> </form> {% endblock %}
registration/password_reset_done.html
{% extends "base.html" %} {% block title %}Password reset successful{% endblock %} {% block content %} <p>We've e-mailed you instructions for setting your password to the e-mail address you submitted.</p> <p>You should be receiving it shortly.</p> {% endblock %}
registration/password_reset_confirm.html
{% extends "base.html" %} {% block title %}Setting New password{% endblock %} {% block content %} {% if validlink %} <p>Please enter your new password twice.<br /> So we can verify you typed it in correctly.</p> <form action="" method="post"> <div style="display:none"> <input type="hidden" value="{{ csrf_token }}" name="csrfmiddlewaretoken"> </div> <table> <tr> <td>{{ form.new_password1.errors }} <label for="id_new_password1">New password:</label></td> <td>{{ form.new_password1 }}</td> </tr> <tr> <td>{{ form.new_password2.errors }} <label for="id_new_password2">Confirm password:</label></td> <td>{{ form.new_password2 }}</td> </tr> <tr> <td></td> <td><input type="submit" value="Change my password" /></td> </tr> </table> </form> {% else %} <h1>Password reset unsuccessful</h1> <p>The password reset link was invalid, <br /> possibly because it has already been used. <br /> Please request a new password reset.</p> {% endif %} {% endblock %}
registration/password_reset_complete.html
{% extends "base.html" %} {% block title %}Password reset complete{% endblock %} {% block content %} <p>Your password has been set. You may go ahead and log in now.</p> <p><a href="{{ login_url }}">Log in</a></p> {% endblock %}
registration/password_reset_email.html
{% autoescape off %} You're receiving this e-mail because you requested a password reset for your user account at {{ site_name }}. Please go to the following page and choose a new password: {% block reset_link %} {{ protocol }}://{{ domain }}{% url django.contrib.auth.views.password_reset_confirm uidb36=uid, token=token %} {% endblock %} Your username, in case you've forgotten: {{ user.username }} Thanks for using our site! The {{ site_name }} team. {% endautoescape %}
SO you're getting password reset system out of the box using only Django included tools. In no time.
Comment me...!
Great tutorial! Thanks!
ReplyDeletethank you so much !!
DeleteThanks!!! it's working!
ReplyDeleteAmazing tutorial!!!
ReplyDeleteBut I receive 2 emails, at the same time. I had a problem like this when I was using signals and sendmail. Do you know what's happening??
Thanks!!!
did you ever fix this? I had the same issue.
DeleteThere's a typo in the registration/password_reset_complete.html:
ReplyDeleteIt says: ">Log in>"
Must say: ">Log in<"
I had to add the 'template_name' in all the URLs, otherwise the custom admin templeates were used. You might clarify how to do to assure that our templates are used before the admin ones...
Thanks for the tutorial.
With testing. E.g. you could add some hook in template if it is the same as admin and stick your tests to that.
DeleteThanks for the error correction.
Well i would like to add something:
ReplyDeleteIn settings.py, if you are using your gmail id then , settings.py should be changed as:
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_PORT = 587
EMAIL_USE_TLS = True
Hi Shashank ! I reported your code inside settings.py file but when I type
Deletepython -m smtpd -n -c DebuggingServer localhost:1025 I got the following errors
SMTP proxy version 0.2
502 Error: command "GET" not implemented
502 Error: command "HOST:" not implemented
502 Error: command "CONNECTION:" not implemented
502 Error: command "ACCEPT:" not implemented
502 Error: command "UPGRADE-INSECURE-REQUESTS:" not implemented
502 Error: command "USER-AGENT:" not implemented
502 Error: command "ACCEPT-ENCODING:" not implemented
502 Error: command "ACCEPT-LANGUAGE:" not implemented
500 Error: bad syntax
Thanks.
the reason why you got that error was because you ran "python -m smtpd -n -c DebuggingServer localhost:1025". you no longer need to run that command once you've set your EMAIL_HOST = 'smtp.gmail.com' because you ain't using localhost nomore.. and remember to allow less secure apps in your gmail settings. follow this link to allow less secure apps https://myaccount.google.com/lesssecureapps
Deletewhat if i want the email sent contain html content?
ReplyDeletethanks
there are 2 email templates in this article... Feel free to change them as you like..
DeleteHi,
ReplyDeleteThat's perfect and everything is working, but I would like to send a html email. By default django is sending only a text email. Is there an easy and internal way to attach a beautiful html version of that email.
Thanks
there are 2 email templates in this article... Feel free to change them as you like..
DeleteIt really helped down here. Cheers from Brazil!
ReplyDeleteWell, I only wish it was higher in Google search list :P
ReplyDeleteWell worked out.
Cheers
I already got it and it's works for me, but i don't want to use smtp port like google
ReplyDeleteEMAIL_HOST = 'smtp.gmail.com'
EMAIL_HOST_USER = ''
EMAIL_HOST_PASSWORD = ''
EMAIL_PORT = 587
EMAIL_USE_TLS = True
it's possible without using this, we send mail
Why not?. Should work out of the box. Depending on your deployment server configuration. And how you send emails from your server.
DeleteThe syntax for:
ReplyDelete{{ protocol }}://{{ domain }}{% url django.contrib.auth.views.password_reset_confirm uidb36=uid, token=token %}
has changed in Django 1.5 to:
{{ protocol }}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb36=uid token=token %}
Thanks
Deletetem como apenas receber a resposta do django da página password_reset_complete.html ? para colocar a resposta dele via ajax no password_reset_confirm.html ?
ReplyDeleteExcellent post !!!
ReplyDeleteNot sure what I'm doing wrong - copied and pasted everything you provide above, but after resetting password, I get:
ReplyDeleteNoReverseMatch at /user/password/reset/
Reverse for 'password_reset_confirm' with arguments '()' and keyword arguments '{u'uidb64': 'Mg', u'token': u'3nh-f59326e4f61d0e42f9d3'}' not found. 1 pattern(s) tried: ['user/password/reset/(?P[0-9A-Za-z]+)/(?P.+)/$']
I did name the URL, as in:
url(r'^user/password/reset/(?P[0-9A-Za-z]+)/(?P.+)/$', 'django.contrib.auth.views.password_reset_confirm',
{'template_name': 'password_reset_confirm.html',
'post_reset_redirect' : 'user/password/done/'},
name='password_reset_confirm'),
Also, not sure how to tell it to use my password_reset_email.html and not django.contrib's
Thanks for writing this up tho!
registration/password_reset_email.html
DeleteThis is a name of a email template. It should be located in the root of your templates folder.
Maybe thing is in your template loaders. You should load YOUR templates first to override admin templates.
Not sure though. Make sure you have inserted all the urls into urls.py accordingly.
Hey Dustin! Im facing the same issue. Ive done whatever has been told. Were you able to fix your issue?
DeleteFacing the same issue
DeleteI used Dustin's modified urlconf entry for the password_reset_confirm (using url() function and naming it), and then used my new named url in the password_reset_email.html file in place of the 'django.contrib.auth.views.password_reset_confirm' in the url tag. That fixed it for me.
DeleteI keep the registration and password reset templates in my project/mainApp/templates/registration folder and it all seems just dandy!
Hi, I had the same issue and solved it by adding the following line to my urls.py
url(r'^reset/done/$', 'django.contrib.auth.views.password_reset_complete', name='password_reset_complete'),
Hi, for anyone facing the same issues you need to change uidb36 to uidb64 in your url.
DeleteYeah, uidb64 needed to be changed. I also had to remove the comma and quote the password_reset_confirm portion like so:
Delete{{ protocol }}://{{ domain }}{% url 'django.contrib.auth.views.password_reset_confirm' uidb64=uid token=token %}
@Dustin I had the same problem but turned out, as lurii suggests, that I just didn't have it in the right templates directory,.
ReplyDeleteOtherwise everything worked exactly as he has it above with two exceptions (for me on Django 1.6 anyway):
1) the url template tag syntax as noted above by "F L"
2) uidb36 in the urls.py and email template both should be uidb64 per https://docs.djangoproject.com/en/1.6/releases/1.6/#django-contrib-auth-password-reset-uses-base-64-encoding-of-user-pk
I did this for and app some months ago and it worked perfectly! I was afraid I couldn't find it again now that I need it, again
ReplyDeleteThank You :D
Thanks! Very helpful!
ReplyDeleteHow can i check whether a email is not registered yet ???
ReplyDeleteAJAX $.get() is not working in template password_reset_form
I can't seem to get any email! I'm using smtp gmail. Any idea why it's not sending the email?
ReplyDeleteWhere do you use Gmail? are other emails pass? How do you use gmail?
DeleteIn general I do not have context to answer your question.
Thanks a lot man !
ReplyDeleteThis works completely fine. Except one thing.
ReplyDeleteIt should throw some error, in-case a user is not registered and trying to reset password.
How to do that ?
how do i resolve this errror??
ReplyDeleteTypeError at /user/password/reset/Mjk-426-d531c711333f26f94afa/
password_reset_confirm() got an unexpected keyword argument 'uidb36'
What a fantastic post - incredibly helpful!!! I made minor alterations for example I had to use uidb64 in all cases because uidb36 is no longer in use: https://docs.djangoproject.com/en/1.8/releases/1.6/
ReplyDeleteAlso in settings.py I had to include DEFAULT_FROM_EMAIL = 'Your Name '
So that I no longer got the error:
SMTPRecipientsRefused: {u'recipient@recipientsdomain.com': (504, '5.5.2 : Sender address rejected: need fully-qualified address')}
This comment has been removed by the author.
DeleteDEFAULT_FROM_EMAIL = ' your name then your email-address-within-angle-brackets'
DeleteThe entire thing in quotes as with all other settings.py values.
This blogger program isn't allowing me to type angle brackets.
Just thought I should add, that in order to override admin template, you need to specify the directory of the app that contains your customized registration/password_reset_form.html etc, in the DIR:[] of template in your settings. https://docs.djangoproject.com/en/1.9/ref/templates/upgrading/
ReplyDeleteThank you for your time, you helped me a lot!
ReplyDeleteLife saver
ReplyDeleteHad to tweak it a little with the answer here : https://stackoverflow.com/questions/35745674/django-password-reset-noreversematch-error
ReplyDeleteWorking perfectly, thanks a lot !
Still very much relevant in 2017. Thanks, Iurrii!
ReplyDelete